Repository navigation
submitPost: preserve native-image submission receipts before hydration and async completion #290
Description
Summary
Could the public SDK expose the original native-image submission receipt independently of Post hydration, and provide a supported completion handle when media creation is asynchronous?
reddit.submitPost() currently returns Promise<Post>. Two paths can reject without exposing a structured post ID or pending handle to the caller:
- Submit returns no ID for an asynchronous image submission;
Post.submitthrows its asynchronous-creation message. - Submit returns an ID, but the SDK's subsequent Info/hydration request fails before the public promise resolves.
For a scheduled publisher, an unknown outcome must not be retried: the post may already exist. This report does not claim every image post fails, or that either mocked response proves Reddit accepted a particular native submission.
Versions and references
- Reproduced locally using the public SDK and RPC mocks from pinned Devvit 0.14.1.
- A separate source-only audit of the published 0.14.8 package found the same async no-ID branch and hydration-before-return behavior. That is not a native reproduction under 0.14.8; no dependency upgrade was performed.
- Public RedditClient reference
- Post implementation
- Published 0.14.1 package, 0.14.8 package
Minimal reproduction: local RPC mocks only
These are direct, supported image-only calls: no application adapter, image/body type suppression, real upload, or live post. Both cases were verified against the pinned SDK using @devvit/test.
import { createDevvitTest } from '@devvit/test/server/vitest';
import { reddit } from '@devvit/web/server';
import { expect, vi } from 'vitest';
const test = createDevvitTest({ subredditName: 'example_dev' });
const options = {
subredditName: 'example_dev',
title: 'SDK receipt reproduction',
kind: 'image' as const,
imageUrls: ['https://i.redd.it/example.png'] as [string],
runAs: 'APP' as const,
};
test('asynchronous no-ID response', async ({ mocks }) => {
const submit = vi.spyOn(mocks.reddit.linksAndComments.plugin, 'Submit')
.mockResolvedValue({ json: { data: {}, errors: [] } });
const info = vi.spyOn(mocks.reddit.linksAndComments.plugin, 'Info');
await expect(reddit.submitPost(options)).rejects.toThrow(
'is being created asynchronously'
);
expect(submit).toHaveBeenCalledTimes(1);
expect(info).not.toHaveBeenCalled();
});
test('Submit ID is not exposed before failed hydration', async ({ mocks }) => {
const submit = vi.spyOn(mocks.reddit.linksAndComments.plugin, 'Submit')
.mockResolvedValue({ json: { data: { id: 'abc123' }, errors: [] } });
const info = vi.spyOn(mocks.reddit.linksAndComments.plugin, 'Info')
.mockRejectedValue(new Error('Mock Info unavailable'));
await expect(reddit.submitPost(options)).rejects.toThrow(
'Mock Info unavailable'
);
expect(submit).toHaveBeenCalledTimes(1);
expect(info).toHaveBeenCalledTimes(1);
});Separate native observation
A private scheduled image submission was recorded as uncertain without a returned post ID. Later read-only inspection found a candidate with the expected image and separate body. The original raw SDK exception/Submit response was not retained, so its precise failure branch and causal candidate binding are unknown. The occurrence remains protected; it was not retried or manually marked as an automatic pass.
This observation is separate from the deterministic mocked reproductions above.
Requested supported contract
- Return the original structured created-post ID before optional Info hydration, so an app can retain it and verify the post separately.
- If native media is pending without an ID, return a documented token with an authenticated completion operation/event tied to that original submission.
- Define created/pending/rejected/unknown and duplicate-completion behavior without another Submit.
The current media API returns an asset ID/URL and documents upload, not post completion. We found no current public receipt hook or pending-media completion contract. If a supported approach already exists, guidance would be appreciated; parsing exception strings, matching the newest post, internal plugin imports, or a second Submit are not safe substitutes for this use case.
Activity