Publishing to social platforms is inherently unreliable — tokens expire, platforms rate-limit, network blips happen. The trick is to fail loudly when it's your bug, and recover quietly when it's not.
Categories of failure
| Category | Examples | What to do |
|---|---|---|
| Your bug | invalid slug, missing field, wrong format | Fix the request; don't retry |
| Auth issue | expired token, revoked OAuth | Notify the user to reconnect |
| Platform issue | rate limit, transient 5xx | Retry with backoff |
| Network issue | DNS, timeout | SDK retries automatically |
Detecting categories
typescript
import { SocifyrError } from '@socifyr/sdk'
try {
await socifyr.uploads.text(params)
} catch (err) {
if (!(err instanceof SocifyrError)) throw err
// Auth issue — surface to user
if (err.message.includes('Token has expired') || err.message.includes('revoked')) {
notifyUser('Please reconnect this platform')
return
}
// Validation / your bug
if (err.status === 400) {
logError(err)
throw err
}
// Rate limit
if (err.status === 429) {
await sleep(60_000)
return retry()
}
// Everything else — log + rethrow
logError(err)
throw err
}Partial failures
When a post fans out to N platforms, some can succeed and others fail:
typescript
const upload = await socifyr.uploads.text(params)
const result = await socifyr.uploads.waitForCompletion(upload.id)
if (result.status === 'partial_failure') {
const failed = result.channels.filter(c => c.result?.status === 'failed')
for (const ch of failed) {
console.warn(`${ch.platform}: ${ch.result?.error}`)
}
// Retry just the failed ones (idempotent — succeeded channels are skipped)
await socifyr.uploads.retry(upload.id)
}When to retry
| Error message contains | Retry? |
|---|---|
rate limit | Yes, after a delay |
timeout | Yes (SDK already retries; consider re-retry from your code if persistent) |
Token has expired | No — user must reconnect |
Token is invalid | No — user must reconnect |
connections should not be empty | No — fix your request |
caption too long | No — fix your content |
| Platform "...is temporarily unavailable" | Yes, after several minutes |
Webhook-driven retries
In production, prefer webhooks over polling. Subscribe to channel.failed and upload.failed to drive retries:
typescript
app.post('/webhooks/socifyr', async (req, res) => {
if (!verifyWebhook(req)) return res.status(401).end()
const { event, data } = req.body
if (event === 'channel.failed' && isRetryableError(data.error)) {
await scheduleRetry(data.uploadId, { delayMinutes: 10 })
}
res.json({ ok: true })
})Logging
Log enough context to debug, never log the API key. The fields you want:
typescript
{
uploadId: upload.id,
connections: params.connections,
status: result.status,
channels: result.channels.map(c => ({
platform: c.platform,
status: c.result?.status,
error: c.result?.error,
})),
}