Hmbown/CodeWhale · error

doctor: error(s)

Error message

doctor: {} error(s): {}

What it means

`codewhale config doctor` validates credentials and endpoint configuration. After printing each individual problem, if any errors were collected it bails with a summary of the count and semicolon-joined list, so the command exits nonzero while still surfacing each finding above the summary line.

Solutions

  1. Read the `error: ...` lines printed immediately before the summary — they name each specific problem.
  2. Fix each reported credential/endpoint issue (e.g. `codewhale providers` set-key flows, correcting the endpoint URL).
  3. Re-run `codewhale config doctor` until it prints 'doctor: credentials and endpoints clean'.

Example fix

null
Defensive patterns

Strategy: try-catch

Validate before calling

// run doctor and inspect before relying on credentials
if !run_config_doctor(&store).is_ok() {
    eprintln!("fix the printed 'error:' lines before making requests");
}

Try / catch

match run_config_doctor(&store) {
    Err(e) if e.to_string().starts_with("doctor: ") => {
        eprintln!("{e}; each problem was printed above as 'error: ...'");
    }
    Err(e) => return Err(e),
    Ok(()) => {}
}

Prevention

When it happens

Trigger: Running `codewhale config doctor` when its checks produced one or more entries in the `errors` vec — e.g. missing or malformed credentials, unreachable or malformed endpoint entries.

Common situations: API key not set or empty; endpoint URL malformed; stale provider credentials after rotation; partially migrated config from an older version.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/47f83062b5108eda. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/lib.rs:4727

            .is_some_and(|value| value.trim().is_empty())
        {
            errors.push(format!("`{name}` is set but empty"));
        }
    }
    for (name, endpoint) in endpoints {
        if let Some(url) = endpoint.as_deref()
            && !url.starts_with("http://")
            && !url.starts_with("https://")
        {
            errors.push(format!("`{name}` is not an http(s) URL: {url}"));
        }
    }

    if !errors.is_empty() {
        for error in &errors {
            println!("error: {error}");
        }
        bail!("doctor: {} error(s): {}", errors.len(), errors.join("; "));
    }
    println!("doctor: credentials and endpoints clean");
    Ok(())
}

fn telemetry_preference_status(preference: Option<bool>) -> &'static str {
    let (enabled, source) = codewhale_config::resolved_telemetry_consent(preference);
    if !enabled {
        return match source {
            codewhale_config::TelemetrySource::Env => "Off (environment or run kill switch)",
            _ => "Off (saved preference)",
        };
    }
    match telemetry::load_setup_state_for_decision() {
        Some(state) if state.telemetry_opted_out() => "Off (saved opt-out)",
        None => "Off (privacy state unreadable)",
        Some(_) => match source {
            codewhale_config::TelemetrySource::Default => "On (default)",

View on GitHub (pinned to 73e0f67d83)