BLUF
band auth status reports authenticated: true for credentials that don't work, and when they're used the failure exits 1 instead of 2. An agent has no reliable way to detect "you need to re-authenticate" — the one auth signal the CLI exposes is wrong in both places.
Reproduce
With a stored-but-revoked client secret:
$ band auth status --plain
{
"authenticated": true,
"profile": "default",
"client_id": "CLI-a3801a13-...",
"account_id": "9901287",
...
}
$ band tendlc campaigns --account-id 9901287 --plain
[account: 9901287]
Error: API request failed: obtaining auth token: token exchange failed (HTTP 401): {"error":"invalid_client","error_description":"Provided client credentials are invalid","error_uri":"https://tools.ietf.org/html/rfc6749#section-5.2"}
Usage:
band tendlc campaigns [flags]
... (full help text) ...
$ echo $?
1
Four distinct problems
1. authenticated means "stored", not "valid". cmd/auth/status.go reports on config contents without ever exchanging the credentials for a token. The field name promises a fact the command never checks. This is the failure that costs the most: an agent reads authenticated: true, branches into a multi-step provisioning workflow, and dies on the first real call.
2. Wrong exit code. internal/cmdutil/exitcodes.go:13 defines ExitAuth = 2, and AGENTS.md documents exit 2 as the re-auth signal. A revoked credential exits 1 (ExitGeneral). Agents branching on exit 2 will never see it; agents branching on exit 1 can't distinguish "re-auth" from any other failure. The 401 happens during token exchange, which apparently sits outside whatever path maps API errors onto the taxonomy.
3. No remediation in the error. The message is three layers of wrapping plus a raw OAuth JSON body, and never says what to do. Compare cmd/tendlc/helpers.go's roleGateError(), which parses 403 bodies into specific, actionable messages — that's the standard this should meet. Expected shape:
Error: your stored credentials were rejected (invalid_client).
The client secret for profile "default" is invalid or has been revoked.
Run: band auth login --client-id <id> --client-secret <secret>
4. Full usage text dumped on a runtime error. The 401 is not a usage mistake, but cobra prints the command's entire help block after it. For an agent parsing stderr this buries the actual error; for a human it implies they typed something wrong. SilenceUsage should be set once the command has begun executing, so usage prints for flag errors and not for runtime failures.
Proposed fix
Make auth status verify. Perform the token exchange and report the result. It's a diagnostic command — one round trip is the right trade, and the token is cached anyway. Follow the tri-state pattern already established for SIP capability rather than overloading a boolean:
{
"authenticated": true,
"token": { "status": "valid", "expires_in": 3480 },
...
}
{
"authenticated": false,
"token": { "status": "rejected", "reason": "invalid_client" },
...
}
Add --no-verify for the offline/fast path that reports stored state only. If verification can't run (network failure, not a credential problem), report "status": "unknown", "reason": "probe_failed" and exit nonzero — don't let an infrastructure blip read as a bad credential.
Route token-exchange 401s to ExitAuth. A 401 invalid_client from the token endpoint is definitionally exit 2. Worth auditing whether other pre-request failures skip the taxonomy the same way.
Agent-nativity requirements
band auth status --plain must be trustworthy enough to gate a workflow on. That's its entire job; today it can't do it.
- Exit 2 must be reachable, and must mean re-auth and nothing else.
- The remediation command belongs in the error text, not just the docs.
- Verification output goes to stdout as structured data; any human-facing narration goes to stderr.
- If
authenticated keeps its current "credentials are stored" meaning, it must be renamed (credentials_stored) — a boolean named authenticated will be read as "I can make API calls" by every consumer, human or model.
Note
Found while designing the 10DLC Registration Center work, where band auth status capabilities are meant to gate whether an account may register campaigns at all. That gating is only as good as this command's honesty.
BLUF
band auth statusreportsauthenticated: truefor credentials that don't work, and when they're used the failure exits 1 instead of 2. An agent has no reliable way to detect "you need to re-authenticate" — the one auth signal the CLI exposes is wrong in both places.Reproduce
With a stored-but-revoked client secret:
Four distinct problems
1.
authenticatedmeans "stored", not "valid".cmd/auth/status.goreports on config contents without ever exchanging the credentials for a token. The field name promises a fact the command never checks. This is the failure that costs the most: an agent readsauthenticated: true, branches into a multi-step provisioning workflow, and dies on the first real call.2. Wrong exit code.
internal/cmdutil/exitcodes.go:13definesExitAuth = 2, and AGENTS.md documents exit 2 as the re-auth signal. A revoked credential exits1(ExitGeneral). Agents branching on exit 2 will never see it; agents branching on exit 1 can't distinguish "re-auth" from any other failure. The 401 happens during token exchange, which apparently sits outside whatever path maps API errors onto the taxonomy.3. No remediation in the error. The message is three layers of wrapping plus a raw OAuth JSON body, and never says what to do. Compare
cmd/tendlc/helpers.go'sroleGateError(), which parses 403 bodies into specific, actionable messages — that's the standard this should meet. Expected shape:4. Full usage text dumped on a runtime error. The 401 is not a usage mistake, but cobra prints the command's entire help block after it. For an agent parsing stderr this buries the actual error; for a human it implies they typed something wrong.
SilenceUsageshould be set once the command has begun executing, so usage prints for flag errors and not for runtime failures.Proposed fix
Make
auth statusverify. Perform the token exchange and report the result. It's a diagnostic command — one round trip is the right trade, and the token is cached anyway. Follow the tri-state pattern already established for SIP capability rather than overloading a boolean:{ "authenticated": true, "token": { "status": "valid", "expires_in": 3480 }, ... }{ "authenticated": false, "token": { "status": "rejected", "reason": "invalid_client" }, ... }Add
--no-verifyfor the offline/fast path that reports stored state only. If verification can't run (network failure, not a credential problem), report"status": "unknown", "reason": "probe_failed"and exit nonzero — don't let an infrastructure blip read as a bad credential.Route token-exchange 401s to
ExitAuth. A401 invalid_clientfrom the token endpoint is definitionally exit 2. Worth auditing whether other pre-request failures skip the taxonomy the same way.Agent-nativity requirements
band auth status --plainmust be trustworthy enough to gate a workflow on. That's its entire job; today it can't do it.authenticatedkeeps its current "credentials are stored" meaning, it must be renamed (credentials_stored) — a boolean namedauthenticatedwill be read as "I can make API calls" by every consumer, human or model.Note
Found while designing the 10DLC Registration Center work, where
band auth statuscapabilities are meant to gate whether an account may register campaigns at all. That gating is only as good as this command's honesty.