Troubleshooting
Exit codes
Stable across versions — script against these. gh and kubectl use a similar split.
| Code | Meaning | Typical cause |
|---|---|---|
| 0 | Success | — |
| 1 | Generic error | Anything not classified — fall back to reading the error message |
| 2 | Usage error | Wrong arguments, unknown flag, unknown config key |
| 3 | Config error | Invalid TOML, missing required key, can't write to ~/.commstate/ |
| 4 | Auth error | Token rejected (401), permission denied (403), expired credentials, no credentials |
| 5 | Network error | DNS failure, connection refused, request timeout |
| 6 | Server error | HTTP 5xx, GraphQL errors not in the auth/not-found buckets |
| 7 | Not found | HTTP 404, resource doesn't exist |
Authentication issues
unauthorized — token rejected (HTTP 401)
Your access token is invalid, expired, or revoked.
# Check whether the token has expired locally
commstate auth whoami
# Re-authenticate
commstate auth login
If the token was a PAT that you revoked from the platform UI, mint a new one:
commstate auth login --with-token <new-pat>
forbidden — your role lacks permission for this action (HTTP 403)
The token is valid but the user doesn't have the permission the endpoint requires.
# What roles do you actually have?
commstate api query '{ me { roles { name } permissions { name } } }'
Two fixes, depending on whether the missing permission was supposed to exist:
- The permission was never seeded for the tenant. Common after enabling a module that ships its own permissions but predates the auto-seeder. Run
php artisan module:seed-permissions --module=<alias> --tenant=<id>on the platform. See Module Lifecycle → Automatic Permission Seeding. - The permission exists but no role grants it to your user. Edit the role in the team UI, or
php artisan tinker --execute='...->givePermissionTo(...)'.
no callback port available — tried [8765 8766 8767 8768 8769]
Another process is using all five fallback ports. Either:
- Find the offender:
lsof -iTCP -sTCP:LISTEN | grep -E '876[5-9]'and stop it. - Use
--no-browserinstead — that flow doesn't need a local listener. - Or
--with-token <PAT>to skip OAuth entirely.
Browser opens but never returns
Two common causes:
- You're on SSH and the browser opened on the remote machine where
localhostdoesn't reach you. Use--no-browser. - Corporate proxy intercepts and rewrites
127.0.0.1redirects. Test withcurl -v http://127.0.0.1:8765/callback?code=test&state=testwhile a login is in progress — you should see "callback already consumed" or a 200. If you see your proxy's error page, ask your proxy team to bypass127.0.0.1for OAuth flows.
device code grant not yet supported by the platform
The --device flag is wired on the CLI side but the backend Passport grant isn't shipped yet. Use the default browser flow (which is what --device would have done UX-wise anyway) or --no-browser.
Login appears to work but whoami shows (unknown)
The token stored OK but /profile failed during login enrichment. Usually means the tenant header was missing or wrong. Re-run with --tenant <slug>.
Network issues
request POST /graphql: connection refused
The configured api_url isn't reachable from this machine.
commstate config get api_url
curl -v $(commstate config get api_url)/graphql -X POST -d '{}'
Check VPN, firewall, and whether the host is up. The API might be running on a different port in dev — commstate health --api-url http://localhost:8000/api/v1 is a quick probe.
validate token: token rejected by server (HTTP 401) immediately on a fresh PAT
Two common causes:
- Wrong API URL. A PAT minted for staging won't work on prod and vice versa.
- Token wasn't fully created. Personal access tokens in Passport are issued via tinker or the team UI — confirm the token shows up in
Settings → Personal access tokens.
GraphQL errors
graphql: Persisted query not found
You sent a sha256Hash for a query that hasn't been registered. Either send the query body the first time (the server stores it under that hash), or check the registered list:
curl -s $(commstate config get api_url)/graphql/persisted \
-H "Authorization: Bearer $(commstate auth whoami -o json | jq -r .token)"
graphql: ad-hoc operations are disabled. Send a persisted query hash.
Lockdown mode is on. The platform refuses any operation not in the persisted store. Register the query you're trying to run, OR ask an operator to disable lockdown for your tenant.
Spatie\Permission\Exceptions\PermissionDoesNotExist
The route requires a permission that doesn't exist in the table. See the 403 fix above — it's the same root cause.
Output / formatting issues
Colors don't render in my terminal
commstate only emits ANSI when stdout is a TTY. If you're piping to less, it won't see colors:
commstate orders list | less -R # -R passes ANSI through
Or force colors off explicitly:
NO_COLOR=1 commstate orders list
commstate orders list -o json is blank
Either the API returned no rows (correct) OR the request failed silently because GraphQL errors got swallowed. Run with -o table first to see the typed error message.
commstate version -o json ignores the format flag
Known v1.0.0 bug. Tracked for v1.0.1. The data is trivial enough you can re-emit it:
commstate version | awk '/^commstate/ {print "{\"version\":\""$2"\"}"}'
Filesystem / permissions
read /home/x/.commstate/credentials: permission denied
Someone (or some setup) chmod'd the file away from 0600. Fix:
chmod 600 ~/.commstate/credentials ~/.commstate/config.toml
keyring: secret service unavailable
Common in headless Linux containers. The CLI auto-falls back to file storage — but tail -f /var/log/syslog may show daemon errors. To force file mode unconditionally, point COMMSTATE_CREDENTIALS_FILE at a writable path:
export COMMSTATE_CREDENTIALS_FILE=$HOME/.commstate/credentials
Diagnostics
commstate version # build identity
commstate config list # current settings
commstate health # API reachability + ring
commstate whoami # stored credential identity (no network)
commstate api query '{ _schema { ring } }' # round-trip GraphQL
If you suspect a regression after upgrading, commstate version shows the commit so you can git log between two reports.
Still stuck
- Check the GraphQL Gateway docs — many CLI 403s are actually a missing permission seed on the tenant.
- Open an issue at the CLI repo with
commstate version, the failing command, and the output with--verbose.