{"record":{"id":"d45ba43df651efa3","repo":"gastownhall/beads","slug":"credential-command-json-has-no-token-access-token","errorCode":null,"errorMessage":"credential command JSON has no token/access_token field","messagePattern":"credential command JSON has no token/access_token field","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/creds/command.go","lineNumber":160,"sourceCode":"// rejected — it is almost always an error message, and using it as a credential\n// would only fail confusingly downstream.\nfunc parseCredential(raw []byte) (token, username string, expiry time.Time, err error) {\n\ttrimmed := bytes.TrimSpace(raw)\n\tif len(trimmed) == 0 {\n\t\treturn \"\", \"\", time.Time{}, fmt.Errorf(\"credential command produced no output\")\n\t}\n\n\tif trimmed[0] == '{' {\n\t\tvar c execCredential\n\t\tif jerr := json.Unmarshal(trimmed, &c); jerr != nil {\n\t\t\treturn \"\", \"\", time.Time{}, fmt.Errorf(\"credential command returned unparseable JSON: %w\", jerr)\n\t\t}\n\t\ttoken = c.Token\n\t\tif token == \"\" {\n\t\t\ttoken = c.AccessToken\n\t\t}\n\t\tif token == \"\" {\n\t\t\treturn \"\", \"\", time.Time{}, fmt.Errorf(\"credential command JSON has no token/access_token field\")\n\t\t}\n\t\tswitch {\n\t\tcase c.ExpirationTimestamp != \"\":\n\t\t\tif t, perr := time.Parse(time.RFC3339, c.ExpirationTimestamp); perr == nil {\n\t\t\t\texpiry = t\n\t\t\t}\n\t\tcase c.ExpiresIn > 0:\n\t\t\texpiry = time.Now().Add(time.Duration(c.ExpiresIn) * time.Second)\n\t\t}\n\t\treturn token, c.Username, expiry, nil\n\t}\n\n\tbare := string(trimmed)\n\tif strings.ContainsAny(bare, \" \\t\\r\\n\") {\n\t\treturn \"\", \"\", time.Time{}, fmt.Errorf(\"credential command output is not a bare token (contains whitespace); expected a token or a JSON {token,expirationTimestamp} envelope\")\n\t}\n\treturn bare, \"\", time.Time{}, nil\n}","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/creds/command.go#L142-L178","documentation":"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.","triggerScenarios":"Helper emits a JSON object with different field names (e.g. 'secret', 'password', 'key') or an object with only expirationTimestamp/username and no token.","commonSituations":"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.","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."],"exampleFix":"// before\n{\"accessTok\":\"ghp_abc\"}\n// after\n{\"access_token\":\"ghp_abc\"}","handlingStrategy":"type-guard","validationCode":"var probe struct {\n    Token       string `json:\"token\"`\n    AccessToken string `json:\"access_token\"`\n}\nif err := json.Unmarshal(raw, &probe); err == nil && probe.Token == \"\" && probe.AccessToken == \"\" {\n    // JSON lacks a token field; fix helper schema first\n}","typeGuard":"func hasCredentialToken(raw []byte) bool {\n    var c struct {\n        Token       string `json:\"token\"`\n        AccessToken string `json:\"access_token\"`\n    }\n    return json.Unmarshal(raw, &c) == nil && (c.Token != \"\" || c.AccessToken != \"\")\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["credentials","json","schema"],"backgroundTag":"credential-json-missing-token-field","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}