Skip to main content

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

ExitCauseWhere to start
0SuccessNothing to do.
1Generic / uncategorizedRe-run with --verbose to see the underlying error.
2Invalid usagehamtrax help <command> -- you're missing a flag or have a typo.
3Auth (missing/expired/revoked)Exit 3
4Rate limitedExit 4
5Tier insufficientExit 5
6QSO cap reachedExit 6
7Network / transportExit 7
8Server error (5xx)Exit 8

See AI Agent Guide → Exit-code table for the agent-friendly version with recovery rules.

Exit 3: auth

Three flavors, distinguished by the server error code in the JSON envelope.

unauthorized -- no key configured

hamtrax: whoami: unauthorized: No API key configured.

The CLI couldn't find a key in any of the resolution-order locations. 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 or via auth panic-revoke. The CLI can't un-revoke it -- generate a new key:

  1. Visit hamtrax.com.
  2. Open the Hamtrax CLI tool.
  3. Generate a fresh key.
  4. 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:

BucketWindowRequestsTriggered by
cliReadPerKey60s120GETs (whoami, list)
cliWritePerKey60s30POSTs (create activation, create contact)
cliDeletePerKey60s10DELETEs (contacts delete)
cliPerIp60s200All authenticated traffic from one IP
cliUnauthPerIp60s20Failed 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 (exit 3 + this error), 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:

  1. Generate a new key at the elevated tier in the web app.

  2. Either swap it into the keychain:

    hamtrax auth set-key htx_live_<elevated>
  3. 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.1 or 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-base with 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_CERTS to 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

--verbose flag

Every command accepts --verbose. It dumps the HTTP method, URL, status, headers, and (truncated) request/response bodies to stderr. The configured API key is never included in verbose output.

hamtrax whoami --verbose 2>&1 | head -20
# > GET https://us-central1-ham-radio-app-b818d.cloudfunctions.net/cliApi/v1/whoami
# > Authorization: Bearer htx_live_***************************
# < 200 OK
# < Content-Type: application/json
# < X-RateLimit-Remaining: 119
# < {"callsign":"N0CALL","plan":"free","tier":"basic","nativeQsoCount":12}

_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://&lt;staging-host&gt;/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 for hamtrax-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 mySig not 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's autoFolderType field.
  • "My script created 5 duplicate activation folders." -- The CLI's activations create is 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-revoke returned exit 3." -- The key was already revoked, or it never existed. There's nothing to do.
  • "contacts list returns cursor but I never asked for one." -- That's how pagination works -- present means there's a next page. Pass it back as --cursor to continue. Absent means you got the last page.

Filing a bug

If you've tried the relevant section above and you're still stuck:

  1. Run with --verbose and capture stderr.
  2. Note the requestId from the error envelope.
  3. Note the CLI version (hamtrax --version) and Node version (node --version).
  4. File at the Hamtrax-CLI GitHub Issues page.

Do not paste your API key into the bug report -- the CLI redacts it from --verbose output, but if you copy from elsewhere, scrub it first.