{"record":{"id":"1744b4b5a96e6a35","repo":"gastownhall/beads","slug":"credential-source-s-w","errorCode":null,"errorMessage":"credential source %s: %w","messagePattern":"credential source (.+?): %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/creds/creds.go","lineNumber":62,"sourceCode":"\t// Name is the provenance slug (env var, file, or command label).\n\tName() string\n\t// Resolve returns the credential when this source is configured. A\n\t// configured=false result means \"not set here, try the next rung\". A non-nil\n\t// error means \"configured but failed\" and aborts the walk — the ladder never\n\t// falls through to a lower-priority rung after an error.\n\tResolve(ctx context.Context) (cred Credential, configured bool, err error)\n}\n\n// ResolveLadder walks sources in priority order and returns the first configured\n// credential. It fails closed: any source error stops the walk and propagates, so a\n// configured-but-broken helper can never silently downgrade to a lower rung. When no\n// source is configured it returns configured=false with no error, letting the caller\n// fall through to a driver-native default (PGPASSWORD, ~/.pgpass, and the like).\nfunc ResolveLadder(ctx context.Context, sources ...Source) (Credential, bool, error) {\n\tfor _, s := range sources {\n\t\tcred, configured, err := s.Resolve(ctx)\n\t\tif err != nil {\n\t\t\treturn Credential{}, true, fmt.Errorf(\"credential source %s: %w\", s.Name(), err)\n\t\t}\n\t\tif configured {\n\t\t\tif cred.Source == \"\" {\n\t\t\t\tcred.Source = s.Name()\n\t\t\t}\n\t\t\treturn cred, true, nil\n\t\t}\n\t}\n\treturn Credential{}, false, nil\n}\n","sourceCodeStart":44,"sourceCodeEnd":73,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/creds/creds.go#L44-L73","documentation":"ResolveLadder walks configured credential sources in order; if a source that reports itself as configured returns an error, the ladder fails closed and wraps the error with the source's name. This distinguishes 'source misconfigured/broken' from 'source not configured', so the caller gets a definitive failure instead of silently falling through.","triggerScenarios":"A Source's Resolve returns err != nil — e.g. the credential-command source's helper fails (1593–1597), or an env/file source fails reading — while configured=true.","commonSituations":"First source in the ladder (e.g. credential_command) is configured but its helper errors; stale credentials in one source poisoning resolution; a config file listing a helper that is no longer installed.","solutions":["Read the wrapped inner error and the source name prefix to see which source failed and why.","Fix or remove the failing source from the configured ladder (config file or env) so the ladder can fall through to later sources.","Re-authenticate or repair the failing credential helper.","Reorder the ladder to try a healthy source first if the broken one is optional."],"exampleFix":"// before\nbd config set credential_command \"gh-helper-missing\"\n// after\nbd config unset credential_command   # let ladder fall through to next source","handlingStrategy":"try-catch","validationCode":"// verify each configured source before relying on the ladder\nfor _, s := range sources {\n    if _, configured, err := s.Resolve(ctx); configured && err != nil {\n        // repair or unset this source first\n    }\n}","typeGuard":null,"tryCatchPattern":"cred, configured, err := creds.ResolveLadder(ctx, srcs...)\nif err != nil {\n    // err text names the failing source; drop/repair it and retry\n    return fmt.Errorf(\"no usable credential: %w\", err)\n}","preventionTips":["Keep the ladder's first source healthy or unset so fall-through works.","Test each configured source individually after credential/config changes.","Remove stale credential_command entries after uninstalling helpers."],"tags":["credentials","config","error-wrapping"],"backgroundTag":"credential-source-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}