gastownhall/beads · error

credential command returned unparseable JSON: %w

Error message

credential command returned unparseable JSON: %w

What it means

When the credential command's output starts with '{', parseCredential treats it as a JSON exec-credential envelope. If json.Unmarshal fails, the JSON is malformed and this error wraps the unmarshal failure. The command is supposed to emit either valid JSON or a bare token.

Source

Thrown at internal/creds/command.go:153

	credCacheMu.Unlock()
	return token, username, expiry, nil
}

// parseCredential extracts the token (and any username/expiry) from a helper's
// stdout. A JSON object is read as the ExecCredential/getToken envelope; otherwise
// the trimmed output is taken as a bare token. A bare value containing whitespace is
// 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
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped %w JSON syntax error to find the malformed offset.
  2. Run the credential command manually and inspect raw stdout for corruption or extra output.
  3. Fix the helper to emit valid JSON: {"token":"...","expirationTimestamp":"...RFC3339..."}.
  4. Alternatively make the helper print just a bare token (no braces) so the JSON path is skipped.

Example fix

// before
{"token: ghp_abc}   // invalid JSON
// after
{"token":"ghp_abc","expirationTimestamp":"2026-01-01T00:00:00Z"}
Defensive patterns

Strategy: validation

Validate before calling

var probe map[string]any
if err := json.Unmarshal(raw, &probe); err != nil {
    // output is not valid JSON; fix helper output first
}

Prevention

When it happens

Trigger: The helper prints a partial/truncated JSON document, pretty-printed output with a trailing error appended, or invalid JSON such as '{token: abc}' without quoting.

Common situations: A custom credential script emitting hand-built JSON with unquoted values; helper mixing JSON output with log lines; proxy/firewall injecting an error page before the JSON.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/69001dd96d22fe7d. Report an issue: GitHub.