{"record":{"id":"78f2bb6f67523eea","repo":"Hmbown/CodeWhale","slug":"gitee-did-not-report-a-pull-request-url-refusing-to-invent","errorCode":null,"errorMessage":"Gitee did not report a pull request URL; refusing to invent one.","messagePattern":"Gitee did not report a pull request URL; refusing to invent one\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/dispatch_runner.rs","lineNumber":658,"sourceCode":"            (\"base\", patch.base_branch.as_str()),\n            (\"body\", body),\n        ])\n        .send()\n        .context(\"could not reach Gitee\")?;\n    let status = response.status();\n    let text = response.text().unwrap_or_default();\n    if !status.is_success() {\n        bail!(\"Gitee pull request create failed (HTTP {status}).\");\n    }\n    let parsed: serde_json::Value =\n        serde_json::from_str(&text).context(\"Gitee returned invalid JSON\")?;\n    parsed\n        .get(\"html_url\")\n        .and_then(serde_json::Value::as_str)\n        .map(str::trim)\n        .filter(|url| url.starts_with(\"https://\"))\n        .map(|url| url.to_string())\n        .ok_or_else(|| anyhow!(\"Gitee did not report a pull request URL; refusing to invent one.\"))\n}\n\nfn open_pr_cnb(\n    slug: &str,\n    job: &CloudJob,\n    patch: &PatchReceipt,\n    title: &str,\n    body: &str,\n) -> Result<String> {\n    let token = read_service_token(\"cnb\").ok_or_else(|| {\n        anyhow!(\"a CNB access token is not configured in the Codewhale service slot; the branch was pushed but no pull request was opened\")\n    })?;\n    let url = validate_outbound_origin(&cnb_pr_url(slug))?;\n    let response = crate::tls::reqwest_blocking_client_builder()\n        .connect_timeout(std::time::Duration::from_secs(8))\n        .timeout(std::time::Duration::from_secs(30))\n        .redirect(reqwest::redirect::Policy::none())\n        .build()","sourceCodeStart":640,"sourceCodeEnd":676,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/dispatch_runner.rs#L640-L676","documentation":"After creating the pull request via the Gitee API, open_pr_gitee extracts html_url from the JSON response and only accepts a trimmed https:// URL; if the response lacks a usable html_url the PR URL is not returned — the code refuses to invent or guess one.","triggerScenarios":"The Gitee create-PR API returns JSON without a usable html_url string field (or with a non-https value) when open_pr_gitee parses the response body.","commonSituations":"Gitee API changes or a proxy/gateway returning a wrapped or partial payload; the token lacks permission so an error body without html_url comes back; a self-hosted Gitee instance serving plain-http URLs.","solutions":["Log/inspect the raw Gitee response body to see what was actually returned, and fix token/permissions if it is an auth error payload.","Retry the request — a transient gateway/CDN response can omit fields.","If the PR was created (check the Gitee UI), record its URL manually; otherwise fix the Gitee instance/API version and re-run."],"exampleFix":"// before\n{\"id\": 123}  // no html_url -> error\n// after\n{\"id\": 123, \"html_url\": \"https://gitee.com/acme/widget/pulls/7\"}","handlingStrategy":"try-catch","validationCode":"// cannot pre-check the response; guard after parsing:\nlet html_url = body.get(\"html_url\").and_then(serde_json::Value::as_str).unwrap_or_default();\nif !html_url.starts_with(\"https://\") { eprintln!(\"Gitee response missing html_url: {body}\"); }","typeGuard":null,"tryCatchPattern":"match open_pr_gitee(slug, job, patch, title, body) {\n    Err(e) if e.to_string().contains(\"refusing to invent\") => {\n        // log raw response; check Gitee UI for the PR before retrying\n    }\n    other => other?,\n}","preventionTips":["Use a token with sufficient Gitee API scopes so responses are complete.","Log raw API bodies on PR creation for diagnosis.","Pin/verify the Gitee instance's API version returns html_url on create."],"tags":["api","gitee","pull-request","response-parsing"],"backgroundTag":"unexpected-response-shape","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T10:30:35.592Z"}