gitroomhq/postiz-app · error · BadBody
${error_message || 'Threads could not process the media'}
Error message
${error_message || 'Threads could not process the media'} What it means
When publishing to Threads, media is staged in a container and polled via checkContainerStatus. If the Graph API reports status ERROR or EXPIRED, a BadBody is thrown with Threads' own error_message (or a generic fallback), meaning Threads rejected or timed out processing the media.
Source
Thrown at libraries/nestjs-libraries/src/integrations/social/threads.provider.ts:185
picture: picture || '',
username: username,
};
}
// Single, read-only status check of a media container - no loops and no
// timers, so the post workflow can poll it with durable timers.
private async checkContainerStatus(
mediaContainerId: string,
accessToken: string
): Promise<'FINISHED' | 'IN_PROGRESS' | 'PUBLISHED'> {
const { status, error_message } = await (
await this.fetch(
`https://graph.threads.net/v1.0/${mediaContainerId}?fields=status,error_message&access_token=${accessToken}`
)
).json();
if (status === 'ERROR' || status === 'EXPIRED') {
throw new BadBody(
this.identifier,
JSON.stringify({ status, error_message }),
'{}',
error_message || 'Threads could not process the media'
);
}
if (status === 'FINISHED' || status === 'PUBLISHED') {
return status;
}
return 'IN_PROGRESS';
}
private async checkLoaded(
mediaContainerId: string,
accessToken: string
): Promise<boolean> {View on GitHub (pinned to 0f1647f749)
Solutions
- Inspect the logged error_message JSON - it names the exact Graph API rejection reason
- Re-encode media to Threads-supported formats (H.264 MP4, JPEG/PNG within size limits)
- Ensure the media URL is publicly reachable by Meta's servers
- Publish immediately after container creation to avoid EXPIRED
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-validate media against Threads limits
if (video.size > 100 * 1024 * 1024) throw new Error('Video exceeds Threads 100MB limit'); Type guard
const isTerminalContainerStatus = (s: string): s is 'ERROR' | 'EXPIRED' => s === 'ERROR' || s === 'EXPIRED';
Try / catch
try { await threads.publish(...); } catch (e) { if (e instanceof BadBody && e.error.includes('error_message')) surfaceToUser(e.error); throw e; } Prevention
- Use JPEG/PNG images and H.264 MP4 within Threads limits
- Publish immediately after container creation
- Keep media URLs publicly accessible to Meta crawlers
When it happens
Trigger: Container status query returns status=ERROR (invalid/corrupt media, unsupported codec, aspect-ratio violation) or status=EXPIRED (the container was never published within its validity window).
Common situations: Uploading HEVC videos or images over Threads' size limits, wrong media_url that Threads cannot fetch, waiting too long between container creation and publish so the container expires.
Related errors
- Threads took too long to process the media, please try again
- LinkedIn ${label} processing failed${status.processingFailur
- LinkedIn took too long to process the media, please try agai
- The media took too long to process, please try again
- The file is corrupted and cannot be uploaded
AI-assisted analysis of gitroomhq/postiz-app@0f1647f749 (2026-08-27).
Data as JSON: /api/errors/dab53b9a9b366aca.
Report an issue: GitHub.