Hmbown/CodeWhale · error · anyhow::Error

CNB did not report a pull request number; refusing to…

Error message

CNB did not report a pull request number; refusing to invent a URL.

What it means

After POSTing to the CNB API, open_pr_cnb parses the JSON response and extracts the `number` field to build the PR URL. If the field is absent, not an integer, or <= 0, it refuses to fabricate a URL and fails. This is an internal-invariant check against silently producing a broken pull-request link.

Solutions

  1. Inspect the raw CNB response text (it is captured in the context chain) to see what was actually returned.
  2. Confirm the CNB endpoint URL and API version; update Codewhale if CNB changed its response schema.
  3. Retry the request; if the server intermittently returns malformed bodies, check for proxies/MITM in front of cnb.cool.
Defensive patterns

Strategy: try-catch

Validate before calling

let parsed: serde_json::Value = serde_json::from_str(&text)?;
if parsed.get("number").and_then(|n| n.as_i64()).filter(|n| *n > 0).is_none() {
    eprintln!("CNB response lacks a valid PR number: {text}");
}

Type guard

fn pr_number(v: &serde_json::Value) -> Option<i64> {
    v.get("number").and_then(serde_json::Value::as_i64).filter(|n| *n > 0)
}

Try / catch

match open_pr_cnb(...) {
    Err(e) if e.to_string().contains("did not report a pull request number") => {
        log::warn!("CNB returned an unexpected body; inspect raw response before retrying");
    }
    other => other?,
}

Prevention

When it happens

Trigger: CNB API returned 2xx JSON without a positive integer `number` field — e.g. an unexpected response shape after an API change, or a proxy returning a non-PR JSON body.

Common situations: CNB server version drift changing the response schema; an auth middleware returning a JSON error body with HTTP 200; hitting the wrong endpoint via a misconfigured cnb_pr_url.

Related errors


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

Appendix: source

Thrown at crates/tui/src/dispatch_runner.rs:700

            "head": job.branch,
            "base": patch.base_branch,
            "body": body,
        }))
        .send()
        .context("could not reach CNB")?;
    let status = response.status();
    let text = response.text().unwrap_or_default();
    if !status.is_success() {
        bail!("CNB pull request create failed (HTTP {status}).");
    }
    let parsed: serde_json::Value =
        serde_json::from_str(&text).context("CNB returned invalid JSON")?;
    let number = parsed
        .get("number")
        .and_then(serde_json::Value::as_i64)
        .filter(|number| *number > 0)
        .ok_or_else(|| {
            anyhow!("CNB did not report a pull request number; refusing to invent a URL.")
        })?;
    Ok(format!("https://cnb.cool/{slug}/-/pulls/{number}"))
}

/// Read a forge token from the Codewhale service slot. Never logged.
fn read_service_token(slot: &str) -> Option<String> {
    codewhale_secrets::Secrets::auto_detect()
        .get(slot)
        .ok()
        .flatten()
        .map(|value| value.trim().to_string())
        .filter(|value| !value.is_empty())
}

fn one_line(value: &str, max: usize) -> String {
    let flat: String = value
        .chars()
        .map(|ch| {

View on GitHub (pinned to 73e0f67d83)