Errors
Handle every SocialError code with its retry disposition, carried context fields, preparation issues, and redacted JSON serialization.
SocialError describes thrown SDK errors with a stable code, operation, and retry disposition. Publication also returns per-target outcomes: adapter and queue failures can appear as failed, unknown, not-submitted, or cancelled instead of throwing. Inspect every outcome. accepted and processing do not confirm publication.
Errors are redacted by design: no credentials, tokens, media, or message content.
Handle publication outcomes and thrown errors
import { createSocial, connectedAccountRef, SocialError } from "@opencoredev/social-sdk";
import { mockBackend } from "@opencoredev/social-sdk/testing";
const social = createSocial({ backend: mockBackend() });
const account = connectedAccountRef({
backend: "default",
platform: "x",
accountId: "mock-account-1",
});
try {
const result = await social.posts.publish({
targets: [{ account }],
content: { text: "Hello from the mock backend" },
});
for (const outcome of result.outcomes) {
switch (outcome.state) {
case "failed":
console.log(outcome.code, outcome.retryDisposition.kind);
// Apply the disposition before considering another submission.
break;
case "unknown":
// Reconcile saved delivery references before retrying.
console.log("Publication needs reconciliation");
break;
case "not-submitted":
console.log("Target was not submitted", outcome.reason);
break;
case "cancelled":
console.log("Target was cancelled");
break;
default:
console.log(outcome.state);
}
}
} catch (error) {
if (!(error instanceof SocialError)) throw error;
switch (error.code) {
case "invalid_input":
// Fix the preparation issues before submitting again.
break;
case "idempotency_conflict":
// Restore the original request or use a key for a new logical publication.
break;
case "unauthorized":
// Verify the server-side grant for the acting account.
break;
default:
console.log(error.toJSON());
}
}
Preparation, authorization, and idempotency errors can throw before dispatch. A returned result preserves each target’s state, including failures after other targets succeeded.
Error codes
| Code | Meaning |
|---|---|
invalid_config |
The adapter or client was constructed with unusable configuration. |
invalid_input |
The request failed local validation before any network call. |
unsupported_capability |
The selected backend does not declare this operation or format. |
missing_permission |
The credential lacks a scope or product permission the operation needs. |
reconnect_required |
The stored authorization is no longer usable; the account must reconnect. |
ineligible_account |
The account class cannot perform the operation, such as a personal account where a professional one is required. |
approval_required |
The operation exists but needs a platform review, audit, or app product. |
rate_limited |
The platform, provider, or the client’s own queue limit rejected the call. |
billing_required |
The provider or platform plan does not cover the operation. |
media_error |
Media validation, upload, or processing failed with a confirmed error. |
not_found |
The referenced resource does not exist on the backend. |
gone |
The resource existed but has been removed. |
upstream_failure |
The platform or provider returned a confirmed server-side failure. |
ambiguous_outcome |
The response was lost after dispatch; the write may have happened. |
cancelled |
The application aborted the operation. |
timeout |
The retry budget’s time allowance elapsed. |
runtime_unsupported |
The current runtime lacks a primitive the operation requires. |
unauthorized |
The credential was rejected outright by the backend. |
idempotency_conflict |
An idempotency key was reused with different content, options, replies, or schedules. |
Retry dispositions
Each error carries a retryDisposition stating what a safe client may do next:
| Disposition | What it means |
|---|---|
{ kind: "never" } |
No safe automatic retry was computed; inspect the outcome and cause. |
{ kind: "after-delay", delayMs } |
Retry after retryDisposition.delayMs, typically for rate limits. |
{ kind: "after-reconnect" } |
Retry only after the account completes a new connection flow. |
{ kind: "reconcile-first" } |
Reconcile the saved reference before deciding; blind retries can double-post. |
Choose retry policy from the disposition and target outcome, rather than the error code alone. never does not prove a later retry cannot succeed: a rate limit without a parsed delay can carry it. The public delay is retryDisposition.delayMs when kind is after-delay.
Honor reconcile-first for writes that may already have happened. A timeout can carry either never or reconcile-first, depending on whether the write is uncertain. Reconcile an unknown outcome before resubmitting; an error code alone does not establish whether a target was published.
What an error carries
Beyond code and message, a SocialError includes the operation name, the backend and account reference where relevant, correlationId for tracing, the provider’s upstreamStatus and upstreamCode when one responded, structured details, and for local validation failures a list of preparation issues pinpointing each rejected field.
Serialization
error.toJSON() returns the redacted, JSON-safe shape with all of the fields above and the retry disposition. Log or transport that shape instead of the raw provider response; it is designed to be safe for application logs.
SocialError.fromJSON(value) turns that shape back into a SocialError, either directly or after JSON.parse, so a queue worker or background job can rethrow a real error with its code and retryDisposition. A value that is not a serialized SocialError throws invalid_input with operation SocialError.fromJSON. The cause is not restored, because toJSON() drops it.
Preparation failures are reported before any network call, so a request with an invalid media source or an over-limit text never reaches the platform. See operations for budgets, cancellation, and diagnostics.