{"record":{"id":"0fcccfb5e2ca6aad","repo":"Hmbown/CodeWhale","slug":"cnb-pull-request-create-failed-http-status","errorCode":null,"errorMessage":"CNB pull request create failed (HTTP {status}).","messagePattern":"CNB pull request create failed \\(HTTP (.+?)\\)\\.","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/dispatch_runner.rs","lineNumber":691,"sourceCode":"        .connect_timeout(std::time::Duration::from_secs(8))\n        .timeout(std::time::Duration::from_secs(30))\n        .redirect(reqwest::redirect::Policy::none())\n        .build()\n        .context(\"could not initialize the CNB client\")?\n        .post(url)\n        .bearer_auth(&token)\n        .json(&serde_json::json!({\n            \"title\": title,\n            \"head\": job.branch,\n            \"base\": patch.base_branch,\n            \"body\": body,\n        }))\n        .send()\n        .context(\"could not reach CNB\")?;\n    let status = response.status();\n    let text = response.text().unwrap_or_default();\n    if !status.is_success() {\n        bail!(\"CNB pull request create failed (HTTP {status}).\");\n    }\n    let parsed: serde_json::Value =\n        serde_json::from_str(&text).context(\"CNB returned invalid JSON\")?;\n    let number = parsed\n        .get(\"number\")\n        .and_then(serde_json::Value::as_i64)\n        .filter(|number| *number > 0)\n        .ok_or_else(|| {\n            anyhow!(\"CNB did not report a pull request number; refusing to invent a URL.\")\n        })?;\n    Ok(format!(\"https://cnb.cool/{slug}/-/pulls/{number}\"))\n}\n\n/// Read a forge token from the Codewhale service slot. Never logged.\nfn read_service_token(slot: &str) -> Option<String> {\n    codewhale_secrets::Secrets::auto_detect()\n        .get(slot)\n        .ok()","sourceCodeStart":673,"sourceCodeEnd":709,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/dispatch_runner.rs#L673-L709","documentation":"`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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Check the logged HTTP status and CNB response body; fix auth first (401/403 usually means a bad or expired CNB token).","Verify the source and target branches exist and differ, and that commits were pushed before PR creation.","Confirm the CNB repo identifier is correct and the token has PR-write permission.","Retry with backoff for 429/5xx responses."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// pre-flight: verify CNB token and refs\nlet ok = check_cnb_repo_and_branches(repo, head, base).await?;\nif !ok { bail!(\"fix CNB repo/refs before creating PR\"); }","typeGuard":null,"tryCatchPattern":"if let Err(e) = open(...) {\n    if e.to_string().contains(\"CNB pull request create failed\") {\n        // parse status; retry on 429/5xx with backoff, fix auth/refs on 4xx\n    }\n}","preventionTips":["Keep CNB credentials current and scoped for PR creation.","Ensure source and target branches exist and were pushed before dispatch.","Rate-limit CNB calls and back off on 429."],"tags":["cnb","http","pull-request","api"],"backgroundTag":"http-error-response","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}