gastownhall/beads · error

credential source %s: %w

Error message

credential source %s: %w

What it means

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.

Source

Thrown at internal/creds/creds.go:62

	// Name is the provenance slug (env var, file, or command label).
	Name() string
	// Resolve returns the credential when this source is configured. A
	// configured=false result means "not set here, try the next rung". A non-nil
	// error means "configured but failed" and aborts the walk — the ladder never
	// falls through to a lower-priority rung after an error.
	Resolve(ctx context.Context) (cred Credential, configured bool, err error)
}

// ResolveLadder walks sources in priority order and returns the first configured
// credential. It fails closed: any source error stops the walk and propagates, so a
// configured-but-broken helper can never silently downgrade to a lower rung. When no
// source is configured it returns configured=false with no error, letting the caller
// fall through to a driver-native default (PGPASSWORD, ~/.pgpass, and the like).
func ResolveLadder(ctx context.Context, sources ...Source) (Credential, bool, error) {
	for _, s := range sources {
		cred, configured, err := s.Resolve(ctx)
		if err != nil {
			return Credential{}, true, fmt.Errorf("credential source %s: %w", s.Name(), err)
		}
		if configured {
			if cred.Source == "" {
				cred.Source = s.Name()
			}
			return cred, true, nil
		}
	}
	return Credential{}, false, nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped inner error and the source name prefix to see which source failed and why.
  2. Fix or remove the failing source from the configured ladder (config file or env) so the ladder can fall through to later sources.
  3. Re-authenticate or repair the failing credential helper.
  4. Reorder the ladder to try a healthy source first if the broken one is optional.

Example fix

// before
bd config set credential_command "gh-helper-missing"
// after
bd config unset credential_command   # let ladder fall through to next source
Defensive patterns

Strategy: try-catch

Validate before calling

// verify each configured source before relying on the ladder
for _, s := range sources {
    if _, configured, err := s.Resolve(ctx); configured && err != nil {
        // repair or unset this source first
    }
}

Try / catch

cred, configured, err := creds.ResolveLadder(ctx, srcs...)
if err != nil {
    // err text names the failing source; drop/repair it and retry
    return fmt.Errorf("no usable credential: %w", err)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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