gastownhall/beads · error
parse gh output: %w
Error message
parse gh output: %w
What it means
Wraps a json.Unmarshal failure when parsing stdout captured from `gh run list`. The library runs the gh CLI, expects a JSON array of GHWorkflowRun objects on stdout, and throws this when gh's output cannot be decoded into []GHWorkflowRun. It indicates gh produced unexpected non-JSON or schema-mismatched output rather than a JSON run list.
Source
Thrown at cmd/bd/gate_discover.go:475
}
if repo != "" {
args = append(args, "--repo", repo)
}
if workflow != "" {
args = append(args, "--workflow", workflow)
}
output, stderr, err := runGH(args...)
if err != nil {
if len(stderr) > 0 {
return nil, fmt.Errorf("gh run list failed: %s", string(stderr))
}
return nil, fmt.Errorf("gh run list: %w", err)
}
var runs []GHWorkflowRun
if err := json.Unmarshal(output, &runs); err != nil {
return nil, fmt.Errorf("parse gh output: %w", err)
}
return runs, nil
}
// matchGateToRun finds the best matching run for a gate using heuristics.
// If the gate has a workflow name hint in AwaitID, only runs matching that workflow are considered.
//
// foreignRepo must be true when runs were queried from a repo other than the
// current one (SF1: a gate whose metadata.repo targets another repository).
// In that case the local commit SHA and branch name are meaningless - they
// describe the current checkout, not the foreign repo - so the commit/branch
// heuristics are skipped entirely rather than comparing against them anyway.
func matchGateToRun(gate *types.Issue, runs []GHWorkflowRun, maxAge time.Duration, foreignRepo bool) *GHWorkflowRun {
workflowHint := getWorkflowNameHint(gate)
// Cross-repo discovery requires a workflow hint. With the commit/branch
// heuristics below neutralized for a foreign repo, a hintless gate couldView on GitHub (pinned to 71377f2769)
Solutions
- Run `gh run list --json ...` manually with the same flags and inspect the raw output for non-JSON text
- Run `gh auth status` / `gh auth login` to fix authentication, the most common cause of non-JSON output
- Upgrade gh CLI (`gh --version`) so all requested --json fields are supported
- Check for shell wrappers/aliases or proxies mangling gh stdout
Example fix
// before: assumes gh always outputs valid JSON
out, err := runGh(ctx, "gh", "run", "list", args...)
if err != nil { return nil, fmt.Errorf("gh run list: %w", err) }
var runs []GHWorkflowRun
if err := json.Unmarshal(output, &runs); err != nil {
return nil, fmt.Errorf("parse gh output: %w", err)
}
// after: fail fast with gh's raw output when it is not JSON
if !json.Valid(bytes.TrimSpace(output)) {
return nil, fmt.Errorf("gh run list: non-JSON output: %s", string(output))
}
var runs []GHWorkflowRun
if err := json.Unmarshal(output, &runs); err != nil {
return nil, fmt.Errorf("parse gh output: %w", err)
} Defensive patterns
Strategy: validation
Validate before calling
raw, _ := exec.Command("gh", "auth", "status").CombinedOutput()
if !strings.Contains(string(raw), "Logged in") {
return errors.New("gh CLI not authenticated; run gh auth login before gate discover")
} Type guard
func isJSONRunList(output []byte) bool {
var runs []GHWorkflowRun
return json.Unmarshal(bytes.TrimSpace(output), &runs) == nil && runs != nil
} Try / catch
runs, err := queryGitHubRunsInRepo(ctx, dir)
if err != nil {
var parseErr *json.UnmarshalTypeError
if errors.As(err, &parseErr) {
return fmt.Errorf("gh output schema mismatch at %v; check gh version and --json fields", parseErr.Struct)
}
return err
} Prevention
- Verify `gh auth status` succeeds before running gate discovery
- Pin a minimum gh CLI version that supports all --json fields the command requests
- Test gate discover with `gh run list --json ...` manually to see raw output on failure
- Avoid shell wrappers or pipes that prepend non-JSON text to gh stdout
When it happens
Trigger: gh run list emits human-readable text (e.g. a login prompt, warning banner, or error message) instead of JSON; gh emits an error object or null where an array is expected; the --json field list passed by the command does not match the GHWorkflowRun struct fields; gh output includes trailing non-JSON lines (progress/auth notices) before the array.
Common situations: Developer is not authenticated with gh (gh prints an auth error to stdout/stderr), gh is an older version lacking a requested --json field, GH_PAGES/enterprise proxy injects a banner, or a partially-installed gh shim echoes shell text.
Related errors
- failed to parse JSONL line: %w
- line %d: %w
- line %d: 'dep' requires a subcommand (add|remove)
- line %d: unknown dep subcommand %q (want add|remove)
- line %d: unsupported batch command %q (supported: close, upd
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/70676aa372ee8443.
Report an issue: GitHub.