Skip to main content
A command failed and you want to know why. She puts errors on stderr with a leading and keeps results on stdout, so a failed command usually stays out of a downstream pipeline. auth:status is the exception: it reports its state on stdout.

Exit codes

auth:status is the exception: it reports Not authenticated. and still exits 0. If a script needs a hard gate before it starts posting, check for connected channels instead:

Reading the error output

Every failed API call is wrapped twice on its way to your terminal. A single line carries all three layers:
  • ❌ Failed to create post tells you which command gave up
  • Request failed is the CLI’s transport wrapper, and it covers network failures too, so it does not on its own mean the server rejected you
  • API Error (400) and the JSON body are the real answer
When you are stuck, the HTTP status is the fastest signal: 401 is authentication, 404 is a wrong ID, and 400 is a bad payload. 429 means you hit the rate limit.

Authentication errors

The API key goes in the Authorization header raw, with no Bearer prefix. If you are hand-rolling requests alongside the CLI, that is the most common mistake.

Flag and input errors

These are caught locally, before any network call.

Post creation errors

Settings and length failures come back in a shape that names the channel that rejected the post, which is what you want when you posted to six at once:
The provider-level checks above (Please fix your settings, post is too long, please fix it) are skipped when type is draft. The settings schema itself is still validated, so a draft with an incomplete --settings payload is still rejected, and the empty-content check applies either way.

Integration and lookup errors

Listing errors

Upload errors

Media has to live on a PostQueen domain before she can attach it, so this is the step most first posts trip on. upload prefixes every failure with Failed to upload file:. Server rejections then add Upload failed (<status>): and the response body.

Analytics returns {"missing": true}

This is not an error. The post published, but the platform never handed back a usable post ID, so PostQueen has nothing to query analytics against. Reconnect it once and analytics work from then on.
1

Ask the provider what it has

You get back an array of recent items with an id and a preview url:
2

Connect the right one

3

Retry analytics

If analytics:post returns an empty array [] instead, the post has not published yet or the provider does not report post-level analytics. Only posts whose release ID is literally missing can be reconnected, and posts:missing returns [] for providers that cannot list recent content.

Common gotchas

Commands print a one-line human-readable header before the JSON, for example 🔌 Connected Integrations: or ✅ File uploaded successfully!. Pipe that straight into jq and it dies on the first token.Drop the header first:
posts:missing is the one command that prints JSON with no header, so it pipes directly.
Wrap JSON arguments in single quotes so the shell leaves the double quotes alone:
Double quotes around the JSON let the shell expand $ and eat the inner quotes, which is what produces Failed to parse settings JSON. For anything long, put it in a file and use --json instead.
-s has no default. Every posts:create needs an explicit ISO 8601 date, including drafts, since the date is what the post gets scheduled for when you promote it later.
Generate one in a script:
Every value you pass to -m, or to an image field in a campaign file, has to be the path value returned by postqueen upload, which is a hosted PostQueen URL. Local filenames are rejected outright. Third-party URLs are refused too on PostQueen Cloud, and on a self-hosted instance only when the operator has set RESTRICT_UPLOAD_DOMAINS; without it they pass validation and fail later at the network instead, because TikTok, Instagram, YouTube and most other providers only accept media served from a domain they can reach.
The tail -n +2 drops the ✅ File uploaded successfully! header line so jq sees valid JSON.This applies to quick tests too. There is no shortcut that skips the upload step.
The CLI guesses a MIME type from the extension when it builds the request, but the API re-detects the real type from the file’s bytes and renames the stored file to match. So a .jpg that is secretly a PDF is rejected with Unsupported file type., and a correctly formatted file with a wrong extension still uploads fine under its true type.Keep extensions accurate anyway. It keeps your own scripts readable and avoids surprises when you inspect the returned path.
A thread that fits on Mastodon will be rejected by X. The limit for each channel is in its settings schema:
Exceeding it returns post is too long, please fix it, naming the provider that complained.
-s splits the same way: it is --date on posts:create and --status on posts:status.Full flag list: Command Reference.
Nothing here matching your error? She is usually fine and the connection to a network usually is not, so start with the channel the message names.

Still stuck?

App troubleshooting

Problems that start in the app rather than in your terminal

Command Reference

Every command, flag, and default in one place

Report a bug

Wrong output or a crash? Open an issue on the CLI repository

Email support

Send the exact command and the full error text