Troubleshooting
Before changing anything, record the action name, requestId, error code, adapter type, and version. Do not include secrets or raw tokens.
Production exits during startup
Check:
- Do
app.baseUrland origins use HTTPS? - Is there a localhost,
0.0.0.0,::,::1, or IPv4-mapped loopback value? - Does the CSRF secret contain at least 32 bytes?
- Does a
__Host-cookie useSecureandPath=/? - Are session time values and the maximum count positive?
npx guildgate-doctor --json
origin_not_allowed
Compare the actual browser Origin value from DevTools Network with configuration, including protocol, host, and port. Do not rewrite origin in the reverse proxy. Verify the CORS allowlist and GuildGate allowlist express the same boundary.
CSRF failure
- Was the token issued for the active session?
- Is the new token used after session rotation?
- Does the header name match what the adapter reads?
- Does the request use
credentials: "include"? - Is cookie domain or path preventing delivery?
Session cookie is cleared repeatedly
Check store timestamps and process clock synchronization. Inspect expiresAt, idleExpiresAt, and rotateAfter. If the Redis prefix or database changed, an old cookie may no longer reach its record.
Per-user session count exceeds the cap
A custom store without SessionStore.create() may race across workers. Perform insert and eviction under the same transaction or Lua operation.
idempotency_conflict
The same key was used with a different request-body hash. Generate a new key for each user intent; preserve the same body and key for a network retry.
idempotency_reservation_lost
The worker lost reservation ownership. Possible causes:
- TTL shorter than action duration
- compare-and-set renewal failure
- Redis or PostgreSQL connectivity loss
- late-settlement window expired
- custom store overwrote by key without owner checks
Before repeating domain work, inspect the stored result and fencing state.
The request timed out but data changed
1.1.0 enforces the response deadline, but underlying work may commit later when it ignores cancellation. Query status or replay with the same idempotency key. Forward context.signal to application I/O and review maximumLateSettlementMs.
Commit succeeded but the UI did not update
Check meta.postCommitIssues on the successful result. Cache or realtime processing may have failed. Do not roll back the domain write or blindly repeat the action. Inspect outbox backlog, publisher status, and hub connection logs.
Transaction callback failure looks like a rollback
A custom adapter may be routing a post-commit callback exception into its rollback catch block. Separate commit from callback execution and implement TransactionPostCommitError or onPostCommitError behavior.
Realtime subscription is denied
- Are handshake origin and session valid?
- Does the channel parser resolve the correct guild ID?
- Is user authorization current?
- Is
maximumSubscriptionsfull? - Did the connection close during asynchronous authorization?
- Does Socket.IO send the
guildgate:subscribeevent name?
Realtime connection closes immediately
Record close code and reason:
1007: invalid JSON1008: authentication, rate, or subscription policy1009: message too large1013: client exceeded backpressure bound
For Socket.IO, a transport with writable=false is treated as a slow client.
Audit paging skips records
Do not use offset pagination. Return the listPage() cursor unchanged and order by (createdAt DESC, id DESC). Do not page records with identical timestamps using the date alone.
Redis tag invalidation misses entries
A custom adapter may not update tags atomically. Old tag membership, the new entry, and new tag membership must change in one script. Do not give the tag index a TTL shorter than the entry lifetime.
CI and local results differ
- Is Node 22 or 24 in use?
- Does
npm ciuse the lockfile? - Are service-test environment variables available?
- Are PostgreSQL 17 and Redis 8 running?
- Do the two live-service definitions skipped locally actually run in CI?
Support record
Include:
- GuildGate and Node versions
- public adapter types
- action name and stable error code
- redacted doctor output
requestId- reproduction steps
Do not include secrets, .env, tokens, cookies, or a complete database URL.