Troubleshooting
Fix the common video failures fast: stuck-on-preparing uploads, blank admin fields, 403s on signed playback, and skipped seeds.
Symptom-first fixes for the failures you are most likely to hit. Find your symptom in the left column, then apply the fix.
| Symptom | Cause | Fix |
|---|---|---|
Video stuck on preparing (status never reaches ready, playbackOptions stays empty) | The upload polls for only ~6s; anything slower waits for the webhook, and the webhook never arrived. Either the endpoint is not publicly reachable, or the signing secret does not match the dashboard — a mismatch is rejected with a 401 and a logged [payload-mux] hint, so check your server logs. | Save the doc again. A save on a doc that has an assetId but no playbackOptions refetches the asset from Mux: if it's ready by now the save lands ready, and if Mux reports it errored the save records that. If it's genuinely still encoding, the doc stays preparing — wait and save again. Recovery is best-effort: if Mux can't be reached the save still succeeds and logs Could not refresh Mux asset …. A doc already marked errored is left alone. Then fix the delivery so it doesn't recur: point the Mux dashboard at a publicly reachable /api/mux/webhook, and in dev tunnel localhost with cloudflared tunnel --url http://localhost:3000 (or ngrok). Confirm MUX_WEBHOOK_SECRET (or MUX_WEBHOOK_SIGNING_SECRET) matches the dashboard's. A verified webhook logs [payload-mux] webhook verified — receiving Mux events once. See Webhook. |
| Uploader field or list-view thumbnail is blank | The collection registers its admin components (uploader field, thumbnail cell) by string path, so a stale import map leaves them unresolved. | Run pnpm payload generate:importmap, then restart the dev server. |
| Signed playback returns 403, or URLs come back unsigned | The signing keys are missing. Under a signed policy each read JWT-signs the playback URLs; without the keys, signing can't happen. | Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY in the env. See Signed playback. |
Boot fails: collections.muxVideo: field(s) … are already defined by the plugin | A field you passed in collections.muxVideo.overrides.fields is named like one the plugin injects: muxUploader, source, title, assetId, status, error, duration, posterTimestamp, aspectRatio, maxWidth, maxHeight, playbackOptions. Fields append on merge, so two same-named fields would otherwise reach Payload as a bare DuplicateFieldName. A colliding field nested in a row, collapsible, or unnamed tab counts too — those share the parent's level. | Rename or remove your field. To reshape a field the plugin owns rather than add one, override the collection key that holds it. |
| Seeding is skipped with a warning | MUX_TOKEN_ID / MUX_TOKEN_SECRET are unset, so the collection is marked seedDisabled. This is expected, not a failure. | Set the credentials and re-run. The next run ingests the clips and wires the refs, with no seed-file changes. See Disabled seeds. |
Behaviors that surprise people
These are working as designed, not bugs.
Deletes propagate both ways. Deleting the Payload doc deletes the Mux asset (afterDelete), and
replacing a video's asset deletes the previous one. The reverse — a Mux-dashboard delete removing the
Payload doc — only fires through the video.asset.deleted webhook. See
The asset lifecycle.
title is unique and auto-deduped. A duplicate title doesn't fail the save — it's suffixed to
stay unique, so the stored title can differ from what was typed. When extending an upload collection,
the deduped title is mirrored onto filename.
Next
- Use a video — relate, play back, and sign playback URLs.
- Endpoints and webhooks — the webhook contract and server-side ingest.
- How it works — the asset lifecycle, revalidation, and seeding.