Troubleshooting
Things that go wrong in production, and how to fix them.
Video stuck in processing
Section titled “Video stuck in processing”Symptom: an upload finished (CF acknowledged), but the asset row stays processing for > 10 minutes.
Diagnosis:
- Check Media → Uploads: is the asset in that queue?
- Check API logs for
[Cron] media-sync tick: is the cron running? - Check CF Stream dashboard: did transcode fail?
Fix:
- Wait one more
media-synctick (runs every minute). The cron reconciles state from CF. - If the asset flips to
failed: click Retry in the uploads UI. If it fails again, delete + re-upload from a different source file. - If the cron isn’t running:
sudo systemctl restart twistcue-api, then check registration logs.
Refund shortfall clamping
Section titled “Refund shortfall clamping”Symptom: a refund executed but underpaidCents > 0: user did not receive full coins back.
Cause: user had already spent the coins before the refund arrived. Ledger cannot take balance below zero; the deduction clamped at what was left.
Fix:
- Accepted behavior: this is by design.
- Track
underpaidCentsin reconciliation. Finance eats the delta on the P&L. - If the customer complains: comp them a subscription or grant equivalent goodwill coins via
wallet:adjust.
Capped pushes (“21,000 targeted, 18,000 delivered”)
Section titled “Capped pushes (“21,000 targeted, 18,000 delivered”)”Symptom: push estimate shows fewer deliverable users than targeted.
Cause: the 3/day cap plus category opt-outs.
Diagnosis: the estimate response breaks it down: targeted / capped / categoryOptedOut / deliverable.
Fix:
- Not a bug: this is the guard rail. See Compose & send a push.
- To reduce capped counts: run fewer parallel campaigns to the same segments; space sends across days.
- Never disable the cap. It’s a product invariant.
insufficient_permissions on a route I should have
Section titled “insufficient_permissions on a route I should have”Symptom: 403 error with { required: ["some:perm"] } on an endpoint you thought your role covered.
Diagnosis:
- Compare your role’s permissions (visible on
/system/roles/:yourRoleId) against the required perm from the error. - Remember: superadmin
*grants everything; specific roles have finite lists. - Support has
wallet:adjustbut notpayments:refund: support requests refunds only.
Fix:
- Ask a superadmin to grant you the missing permission, or use a different admin account.
- If you believe the endpoint requires the wrong perm, check
packages/contracts/src/rbac.tsand the@RequirePermissiondecorator on the controller method.
Approval stuck pending
Section titled “Approval stuck pending”Symptom: an approval row has been pending for hours/days.
Diagnosis:
- Nobody with the right permission has acted on it.
- Or: the required approver is out; no other admin holds the perm.
- Or: the approver is the same person as the requester (
approver_must_differon approve attempts).
Fix:
- Ping a second admin with the right perm.
- If nobody else has it: superadmin can approve (superadmin has
*). - Rejecting is also valid: reject with a reason, requester resubmits later.
token_expired after 12 hours
Section titled “token_expired after 12 hours”Expected. Re-login. If it happens sooner, someone bumped token_version (force-logout of your admin account). Check with a superadmin.
Wallet balance looks wrong
Section titled “Wallet balance looks wrong”Symptom: the user’s balance on User 360 doesn’t match what they see on mobile.
Diagnosis:
- Mobile might be cached. Ask user to swipe-refresh.
- Or: a webhook is stuck (see the transactions view for the user’s pending purchases).
- Or: a scheduled promo/grant hasn’t fired yet.
Fix:
- Refresh the ledger view on User 360; it’s authoritative.
- Compare against the ledger explicitly:
SELECT SUM(amount) FROM coin_ledger WHERE user_id = ? AND account LIKE 'wallet_%'. - Never manually “correct” the balance. Adjust the ledger via the wallet-adjust flow.
insufficient_balance when deducting coins
Section titled “insufficient_balance when deducting coins”Cause: the deduction would take the user below zero. Server refuses.
Fix:
- Deduct only what’s there. If you need to reclaim more (fraud), use multiple partial deductions as the wallet refills, or accept the shortfall.
- For a full clawback context, ban the user: banned users cannot spend the residual either way.
Series won’t publish: “incomplete localization”
Section titled “Series won’t publish: “incomplete localization””Symptom: publish workflow banner says a target market’s language is missing.
Fix:
- Fill the missing
title_i18n[lang]andsynopsis_i18n[lang]for the market. - Or narrow the series
market_availabilityto only markets where the localization is complete. - Superadmin can force-publish (soft warning, not hard block); audit log will flag it.
Publishing scheduled for wrong time
Section titled “Publishing scheduled for wrong time”Cause: almost always timezone confusion. Server is UTC.
Fix:
- Check
publish_schedules.run_at: should be UTC. - Unschedule → reschedule with correct UTC time.
zero-sum breach red banner on Economy monitor
Section titled “zero-sum breach red banner on Economy monitor”Never ignore this. Means a ledger event was written with rows that don’t sum to zero: a code bug.
Action:
- Screenshot the banner.
- Find the offending
event_id(economy monitor detail; orSELECT event_id, SUM(amount) FROM coin_ledger GROUP BY event_id HAVING SUM(amount) <> 0). - Escalate to engineering immediately with the event_id and time window.
Login rate-limited
Section titled “Login rate-limited”Symptom: 429 too_many_attempts on POST /admin/auth/login.
Cause: 10 attempts per 5 minutes per IP.
Fix: wait 5 minutes or use a different IP. If persistent, someone’s trying to brute-force: check the API logs.