gastownhall/beads · error
credential command JSON has no token/access_token field
Error message
credential command JSON has no token/access_token field
What it means
If the credential command's output parses as JSON but contains neither a 'token' nor an 'access_token' field, parseCredential rejects it. The JSON envelope contract requires at least one of these fields to carry the credential.
Source
Thrown at internal/creds/command.go:160
// rejected — it is almost always an error message, and using it as a credential
// would only fail confusingly downstream.
func parseCredential(raw []byte) (token, username string, expiry time.Time, err error) {
trimmed := bytes.TrimSpace(raw)
if len(trimmed) == 0 {
return "", "", time.Time{}, fmt.Errorf("credential command produced no output")
}
if trimmed[0] == '{' {
var c execCredential
if jerr := json.Unmarshal(trimmed, &c); jerr != nil {
return "", "", time.Time{}, fmt.Errorf("credential command returned unparseable JSON: %w", jerr)
}
token = c.Token
if token == "" {
token = c.AccessToken
}
if token == "" {
return "", "", time.Time{}, fmt.Errorf("credential command JSON has no token/access_token field")
}
switch {
case c.ExpirationTimestamp != "":
if t, perr := time.Parse(time.RFC3339, c.ExpirationTimestamp); perr == nil {
expiry = t
}
case c.ExpiresIn > 0:
expiry = time.Now().Add(time.Duration(c.ExpiresIn) * time.Second)
}
return token, c.Username, expiry, nil
}
bare := string(trimmed)
if strings.ContainsAny(bare, " \t\r\n") {
return "", "", time.Time{}, fmt.Errorf("credential command output is not a bare token (contains whitespace); expected a token or a JSON {token,expirationTimestamp} envelope")
}
return bare, "", time.Time{}, nil
}View on GitHub (pinned to 71377f2769)
Solutions
- Update the credential helper to include a "token" or "access_token" field in its JSON output.
- If the helper outputs the token under another key, add a mapping/shim script that renames it to "token".
- Check the helper's documentation/version for its current output schema.
- As a workaround, have the helper print a bare token instead of JSON.
Example fix
// before
{"accessTok":"ghp_abc"}
// after
{"access_token":"ghp_abc"} Defensive patterns
Strategy: type-guard
Validate before calling
var probe struct {
Token string `json:"token"`
AccessToken string `json:"access_token"`
}
if err := json.Unmarshal(raw, &probe); err == nil && probe.Token == "" && probe.AccessToken == "" {
// JSON lacks a token field; fix helper schema first
} Type guard
func hasCredentialToken(raw []byte) bool {
var c struct {
Token string `json:"token"`
AccessToken string `json:"access_token"`
}
return json.Unmarshal(raw, &c) == nil && (c.Token != "" || c.AccessToken != "")
} Prevention
- Use the documented schema: {"token":"...","expirationTimestamp":"RFC3339"}.
- Include "access_token" as an alternate key if your helper uses OAuth naming.
- Add a contract test asserting the helper output contains a token field.
When it happens
Trigger: Helper emits a JSON object with different field names (e.g. 'secret', 'password', 'key') or an object with only expirationTimestamp/username and no token.
Common situations: Custom credential scripts written against a different helper schema (e.g. docker's credsStore format with ServerURL/Username/Secret fields); version change where the helper renamed its output field.
Related errors
- unable to parse plugin file v2: %w
- failed to parse issue from JSONL: %w
- credential command returned unparseable JSON: %w
- failed to parse issues response: %w
- failed to parse team states response: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/d45ba43df651efa3.
Report an issue: GitHub.