{"record":{"id":"67019a5be03c80a9","repo":"BoundaryML/baml","slug":"no-download-url-for-checksum-asset","errorCode":null,"errorMessage":"No download URL for checksum asset","messagePattern":"No download URL for checksum asset","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/playground-server/src/server.rs","lineNumber":182,"sourceCode":"        .find(|a| a[\"name\"].as_str() == Some(&web_panel_asset_name))\n        .ok_or_else(|| anyhow::anyhow!(\"No asset named '{}' in release\", web_panel_asset_name))?;\n    let download_url = asset[\"browser_download_url\"]\n        .as_str()\n        .ok_or_else(|| anyhow::anyhow!(\"No download URL for asset\"))?;\n\n    // Find the checksum asset\n    let checksum_asset = assets\n        .iter()\n        .find(|a| a[\"name\"].as_str() == Some(&checksum_asset_name))\n        .ok_or_else(|| {\n            anyhow::anyhow!(\n                \"No checksum asset named '{}' in release\",\n                checksum_asset_name\n            )\n        })?;\n    let checksum_url = checksum_asset[\"browser_download_url\"]\n        .as_str()\n        .ok_or_else(|| anyhow::anyhow!(\"No download URL for checksum asset\"))?;\n\n    tracing::info!(\n        \"Found assets - main: {}, checksum: {}\",\n        download_url,\n        checksum_url\n    );\n\n    // Compute extraction directory using the provided version\n    let home =\n        dirs::home_dir().ok_or_else(|| anyhow::anyhow!(\"Could not determine home directory\"))?;\n    let extract_root: PathBuf = home\n        .join(\".baml/playground\")\n        .join(format!(\"web-panel-dist-{version}\"));\n    let dist_dir = extract_root.join(\"dist\");\n\n    // If already extracted, return the correct directory\n    if dist_dir.exists() && dist_dir.read_dir()?.next().is_some() {\n        tracing::info!(\"Web panel already extracted at: {}\", dist_dir.display());","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/playground-server/src/server.rs#L164-L200","documentation":"Having found the checksum asset, get_playground_dist reads checksum_asset[\"browser_download_url\"] as a string to know where to download it. If that field is missing or not a string, this error is returned. Like the main asset's download URL check, this guards against malformed asset objects.","triggerScenarios":"The matched checksum asset JSON has no string browser_download_url field — synthetic/mocked payloads or unexpected API response shape.","commonSituations":"Mocked GitHub responses for tests missing the field; intermediary proxies altering the JSON; GitHub API schema drift.","solutions":["Inspect the checksum asset JSON and confirm browser_download_url exists; fix synthetic payloads if applicable.","Construct the download URL from the release/asset IDs as a fallback.","Update the parsing logic if the GitHub API response shape changed."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// preflight: confirm the checksum asset has a download URL\nlet v: serde_json::Value = reqwest::get(&api_url).await?.json().await?;\nlet ok = v[\"assets\"].as_array().map_or(false, |a| {\n    a.iter().any(|x| x[\"name\"].as_str().map_or(false, |n| n.ends_with(\".sha256\"))\n        && x[\"browser_download_url\"].is_string())\n});\nif !ok { eprintln!(\"Checksum asset missing browser_download_url\"); }","typeGuard":"fn checksum_url<'a>(release: &'a serde_json::Value, name: &str) -> Option<&'a str> {\n    release[\"assets\"].as_array()?.iter()\n        .find(|a| a[\"name\"].as_str() == Some(name))?\n        [\"browser_download_url\"].as_str()\n}","tryCatchPattern":"match get_playground_dist().await {\n    Err(e) if e.to_string().contains(\"No download URL for checksum asset\") => {\n        eprintln!(\"Malformed checksum asset payload — inspect the API response\");\n    }\n    other => other?,\n}","preventionTips":["Validate checksum asset JSON shape in tests","Avoid mocks that omit browser_download_url","Derive the checksum URL from the asset name as fallback","Update parsing if the GitHub API schema changes"],"tags":["github-api","json","unexpected-response","rust"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}