Hmbown/CodeWhale · error

CNB pull request create failed

Error message

CNB pull request create failed (HTTP {status}).

What it means

`open_pr_cnb` POSTs a pull-request-create request to the CNB API; a non-success HTTP status causes a bail carrying the status code. On success the code parses the JSON body and reads `number` to build the PR URL.

Solutions

  1. Check the logged HTTP status and CNB response body; fix auth first (401/403 usually means a bad or expired CNB token).
  2. Verify the source and target branches exist and differ, and that commits were pushed before PR creation.
  3. Confirm the CNB repo identifier is correct and the token has PR-write permission.
  4. Retry with backoff for 429/5xx responses.
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: verify CNB token and refs
let ok = check_cnb_repo_and_branches(repo, head, base).await?;
if !ok { bail!("fix CNB repo/refs before creating PR"); }

Try / catch

if let Err(e) = open(...) {
    if e.to_string().contains("CNB pull request create failed") {
        // parse status; retry on 429/5xx with backoff, fix auth/refs on 4xx
    }
}

Prevention

When it happens

Trigger: Calling `open` with the CNB provider while the CNB API returns 4xx/5xx for the PR-create POST (auth failure, invalid repo/branch, validation rejection, server error).

Common situations: Missing or expired CNB credentials; source or target ref does not exist; repo slug wrong after rename; CNB outage or rate limit returning 429/5xx.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: source

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

        .connect_timeout(std::time::Duration::from_secs(8))
        .timeout(std::time::Duration::from_secs(30))
        .redirect(reqwest::redirect::Policy::none())
        .build()
        .context("could not initialize the CNB client")?
        .post(url)
        .bearer_auth(&token)
        .json(&serde_json::json!({
            "title": title,
            "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()

View on GitHub (pinned to 73e0f67d83)