Troubleshooting
When the CLI fails, it fails with a deterministic exit code and a structured error envelope. This page walks through the most common failures, what they mean, and how to recover.
Quick reference: exit codes
| Exit | Cause | Where to start |
|---|---|---|
0 | Success | Nothing to do. |
1 | Generic / uncategorized | Re-run the command with --json and capture stderr. |
2 | Invalid usage | hamtrax help <command> -- you're missing a flag or have a typo. |
3 | Auth (missing/expired/revoked) | Exit 3 |
4 | Rate limited | Exit 4 |
5 | Tier insufficient | Exit 5 |
6 | QSO cap reached | Exit 6 |
7 | Network / transport | Exit 7 |
8 | Server error (5xx) | Exit 8 |
See AI Agent Guide → Exit-code table for the agent-friendly version with recovery rules.
Exit 3: auth
Three common flavors, distinguished by the CLI or server error code in stderr.
auth_missing -- no key configured
hamtrax: whoami: auth_missing: No API key configured. Run `hamtrax auth login`.
The CLI couldn't find a key in any of the resolution-order locations before making a network request. Fix:
hamtrax auth login
# or
export HAMTRAX_API_KEY=htx_live_xxxxxxxxxxxxxxxxxxxxxxxx
See Auth and Keys → Resolution order.
unauthorized -- malformed key
hamtrax: whoami: unauthorized: Missing or malformed Authorization header.
The configured key doesn't start with htx_live_ or has the wrong length. You probably copy-pasted only part of it. Re-copy from the web app reveal modal, or generate a fresh key (the old one is unrecoverable -- keys are shown once).
key_revoked
hamtrax: whoami: key_revoked: API key has been revoked.
The key was revoked in the web app. The CLI can't un-revoke it -- generate a new key:
- Visit hamtrax.com.
- Open the Hamtrax CLI tool.
- Generate a fresh key.
hamtrax auth set-key htx_live_<new>.
Exit 4: rate limited
hamtrax: contacts list: rate_limited: Too many requests. Please try again in 47s.
Retry-After: 47
You hit one of the per-key or per-IP rate-limit buckets. The buckets and their windows:
| Bucket | Window | Requests | Triggered by |
|---|---|---|---|
cliReadPerKey | 60s | 120 | GETs (whoami, list) |
cliWritePerKey | 60s | 30 | POSTs (create activation, create contact) |
cliDeletePerKey | 60s | 10 | DELETEs (contacts delete) |
cliPerIp | 60s | 200 | All authenticated traffic from one IP |
cliUnauthPerIp | 60s | 20 | Failed auth attempts (anti-grinding) |
Fix: wait Retry-After seconds and retry. The CLI prints the value; an agent should parse it from the error envelope's details.resetSeconds and back off.
# Manual retry pattern
until hamtrax contacts list --folder $FOLDER_ID; do sleep 60; done
If you're seeing cliUnauthPerIp, you're sending failed auth attempts too fast -- the right move is to fix your key, not to retry blindly.
Exit 5: tier insufficient
hamtrax: contacts delete: tier_insufficient: Requires 'elevated' tier.
You tried contacts delete (or another elevated operation) with a basic key. Tiers are fixed at key creation; there's no upgrade path. Fix:
-
Generate a new key at the elevated tier in the web app.
-
Either swap it into the keychain:
hamtrax auth set-key htx_live_<elevated> -
Or scope it to one command via env var (recommended for one-off cleanups):
HAMTRAX_API_KEY=htx_live_<elevated> hamtrax contacts delete qso_92b1...
Then revoke the old basic key if you're done with it. See Examples → Switch tiers.
Exit 6: QSO cap reached
hamtrax: contacts create: qso_cap_reached: Free-tier QSO limit reached. Upgrade to continue logging.
{
"details": { "current": 200, "limit": 200 }
}
You're on the free tier and you've logged 200 native QSOs (web + CLI combined). Imported QSOs don't count -- only ones you logged yourself.
Fix: subscribe at hamtrax.com/account. Once your subscription is active (active or trialing), the cap lifts and the next CLI call goes through. No CLI restart required -- the check is per-request.
If you're a Hamtrax founder (isFounder: true on your user doc), you should never hit this; if you do, file a support ticket.
Exit 7: network
hamtrax: whoami: network error: getaddrinfo ENOTFOUND ...
The CLI couldn't reach the Hamtrax API. Common causes:
- No internet connection. Check
ping 1.1.1.1or your usual probe. - Firewall blocking
*.cloudfunctions.net. Some corporate networks do this. Talk to your network admin or use a different network. - DNS resolver misconfigured. Try
--api-basewith a different region or staging URL if you have one. - TLS interception (mitm). Some enterprise environments rewrite TLS certs. The CLI uses Node's default trust store; if your org installs a custom root CA, set
NODE_EXTRA_CA_CERTSto point at it.
For an agent: retry with exponential backoff up to 3 attempts. If all 3 fail, surface to the user.
Exit 8: server error
hamtrax: contacts create: internal: Internal error.
Something blew up server-side. The error envelope includes a requestId (a UUID) -- include this when filing a support ticket so we can correlate logs.
{
"error": "internal",
"message": "Internal error",
"requestId": "f9c0a1d4-7b3e-4c45-a8d2-12e3a45b6c78"
}
For an agent: retry once after a short delay; if it persists, surface to the user with the requestId.
Debug tips
Structured errors with --json
For data commands, add --json and capture stderr. Non-zero exits use a stable CLI error envelope with command, error, message, and exitCode; API errors may also include status, details, requestId, and retryAfter.
The published 0.1.1 CLI accepts --verbose, but it does not emit a detailed HTTP request/response trace.
_health endpoint
A quick connectivity probe that doesn't consume your rate-limit budget:
curl -s https://us-central1-ham-radio-app-b818d.cloudfunctions.net/cliApi/v1/_health
# {"version":"v1","time":"2026-05-07T15:42:11.000Z"}
If _health is reachable but every other call fails with exit 3, your key is the problem -- not the network.
--api-base for staging
Hamtrax developers can point the CLI at a non-production environment:
hamtrax --api-base https://<staging-host>/cliApi whoami
Keys are environment-scoped -- a production key won't authenticate against staging.
Inspecting the keychain
If you suspect the keychain entry is corrupt:
- macOS: open Keychain Access, search for
hamtrax-cli. - Windows: open Credential Manager, look under Generic Credentials for
hamtrax-cli. - Linux (GNOME):
seahorse(Passwords and Keys), search forhamtrax-cli.
You can delete the entry by hand and re-run hamtrax auth login.
Bypass keychain entirely
For headless / CI debugging:
HAMTRAX_NO_KEYRING=1 HAMTRAX_API_KEY=htx_live_xxx hamtrax whoami
This skips the keychain probe (which can hang on systems without a Secret Service daemon) and the config-file lookup, going straight to the env var.
Common mistakes
- "Why is
mySignot set on my CLI-logged QSOs?" -- It is, but only when you log into an activation folder. If you log into a category or monthly folder, no auto-population happens. Check the folder'sautoFolderTypefield. - "My script created 5 duplicate activation folders." -- The CLI's
activations createis idempotent on(callsign, locationReference, startDate-UTC). If you're seeing duplicates, you're varying one of those fields between calls (different callsigns, references, or running across UTC date boundaries). Pin them and try again. - "
auth panic-revokedidn't revoke my key." -- Server-side key revocation is not available from the0.1.1CLI. Use the web app's CLI security page;auth logoutonly removes the local copy. - "
contacts listreturnscursorbut I never asked for one." -- That's how pagination works -- present means there's a next page. Pass it back as--cursorto continue. Absent means you got the last page.
Filing a bug
If you've tried the relevant section above and you're still stuck:
- Run the failing command with
--jsonand capture stderr. - Note the
requestIdfrom the error envelope. - Note the CLI version (
hamtrax --version) and Node version (node --version). - File at the Hamtrax-CLI GitHub Issues page.
Do not paste your API key into the bug report. The CLI's structured errors do not include the plaintext key, but if you copy from elsewhere, scrub it first.