❌ 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
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 posttells you which command gave upRequest failedis the CLI’s transport wrapper, and it covers network failures too, so it does not on its own mean the server rejected youAPI Error (400)and the JSON body are the real answer
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
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
jq fails because the output starts with a header line
jq fails because the output starts with a header line
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.JSON in the shell
JSON in the shell
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.The date is required and must be ISO 8601
The date is required and must be ISO 8601
-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.Media must be uploaded to PostQueen first
Media must be uploaded to PostQueen first
Every value you pass to The
-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.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.File type is detected from content, not from the extension
File type is detected from content, not from the extension
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.Character limits differ per platform
Character limits differ per platform
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.-d means different things on different commands
-d means different things on different commands
-s splits the same way: it is --date on posts:create and --status on posts:status.Full flag list: Command Reference.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