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 input or folder hygiene | Exit 2 -- fix the input/destination; don't retry blindly. |
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 2: validation
Exit 2 can mean a missing/bad flag, or a server-side filing rejection. Re-read the structured message before changing anything. Contact creation is rejected when the destination is All Contacts or a category, when a contact's UTC month does not match a monthly folder, or when an activation folder's program/reference identity is unsupported, ambiguous, or malformed. Fix the folder metadata or choose the intended contact-holding folder with the user; never silently file the QSO somewhere else.
hamtrax contacts create ... --json
# {"error":"validation_error","message":"Contacts cannot be placed in \"Activations\""}
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
Exit 4 covers both short request bursts and the logbook's sustained account safety budgets. Inspect details.reason before deciding whether to retry.
The short per-key and per-IP buckets and their windows are:
| 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) |
For a short bucket response, wait Retry-After seconds. For details.reason: "daily_logbook_budget", the same header and details.retryAfterSeconds point to the next UTC reset; preserve the exact command and retry after that delay. Browser, CLI, and automatic imports share this sustained account budget.
For details.reason: "lifetime_logbook_budget", stop. This emergency circuit breaker has no reset timer, so the response deliberately omits Retry-After and includes supportRequired: true. Preserve the command and contact Hamtrax support; blindly retrying cannot clear the hold.
# Manual retry pattern for a resettable burst response only
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?" -- POTA fields are stamped for you only when you log into a valid POTA activation folder. Everywhere else you send them yourself:POST /v1/contactsacceptsmySig,mySigInfo,sig, andsigInfo. A custom folder imposes no program hygiene, but a monthly hunting folder should carry the worked station'ssig/sigInfo-- a contact filed there without program identity passes the month check and then reads as an unrecognized program, missing from hunt duplicate detection. Category and All Contacts folders reject contact creation because they are containers. If an activation folder has missing, malformed, or conflicting program/reference data, the CLI rejects the save instead of guessing. That folder is the one the web app lists under Unrecognized program; when the park reference is at fault, correct the park there (see Fixing the Wrong Park) and the CLI will accept saves into it again. - "My script created 5 duplicate activation folders." -- The CLI's
activations createis idempotent on the program, callsign, normalized reference, and UTC start day. If you're seeing duplicates, you're varying one of those values between calls or running across UTC date boundaries. Pin them and try again -- pass one explicit--start-timefor the whole session so a run that crosses 0000 UTC keeps resolving to the folder it opened. A conflicting or malformed existing folder returns a validation error instead of being adopted. The live Hamtrax logger keeps a recorded session that crosses 0000 UTC in one folder, and Import Contacts rejoins that folder when the recorded span already exists. A new import with no matching span is organized by UTC day;activations createresolves from the explicit start time you supply. - "
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.