{"record":{"id":"f55f433ddda96428","repo":"Yeachan-Heo/oh-my-codex","slug":"manifest-url-basename-mismatch","errorCode":"manifest_url_basename_mismatch","errorMessage":"[native-assets] manifest_url_basename_mismatch: ${asset.download_url}","messagePattern":"\\[native-assets\\] manifest_url_basename_mismatch: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/native-assets/policy.ts","lineNumber":187,"sourceCode":"    const key = nativeReleaseAssetLogicalKey(asset);\n    if (logicalKeys.has(key)) throw policyError('manifest_duplicate_logical_key', asset.archive);\n    logicalKeys.add(key);\n    const basename = nativeReleaseAssetBasename(asset);\n    if (asset.archive !== basename || !nativeArchiveSuffix(basename)) {\n      throw policyError('manifest_archive_invalid', asset.archive);\n    }\n    if (archiveHintMismatch(asset, basename)) throw policyError('manifest_archive_hint_mismatch', basename);\n    if (basenames.has(basename)) throw policyError('manifest_duplicate_archive_basename', basename);\n    basenames.add(basename);\n    let downloadUrl: URL;\n    try {\n      downloadUrl = new URL(asset.download_url);\n    } catch {\n      throw policyError('manifest_invalid_url', asset.download_url);\n    }\n    if ((downloadUrl.protocol !== 'https:' && downloadUrl.protocol !== 'http:')\n      || downloadUrl.pathname.split('/').at(-1) !== basename) {\n      throw policyError('manifest_url_basename_mismatch', asset.download_url);\n    }\n  }\n}\n","sourceCodeStart":169,"sourceCodeEnd":191,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/native-assets/policy.ts#L169-L191","documentation":"The download URL's last path segment must equal the archive basename and its protocol must be http or https. This ties the URL to the validated archive identity and blocks exotic protocols.","triggerScenarios":"download_url whose pathname ends in a different filename than asset.archive, or protocols like file: or ftp:.","commonSituations":"Redirect-style URLs, query-string-only links, uploading the file under a different name than declared, or a mirror URL that appends tokens after the filename in the path.","solutions":["Make the URL's final path segment exactly match asset.archive","Use http/https only","If the host rewrites filenames, download to a temp name and rename, and update the manifest to the real URL"],"exampleFix":"// before\n\"archive\":\"omxd-linux-x64.tar.zst\",\n\"download_url\":\"https://dl.acme.io/latest?asset=omxd-linux-x64\"\n// after\n\"archive\":\"omxd-linux-x64.tar.zst\",\n\"download_url\":\"https://dl.acme.io/v1/omxd-linux-x64.tar.zst\"","handlingStrategy":"validation","validationCode":"const ok = assets.every((a) => {\n  try { const u = new URL(a.download_url); return (u.protocol==='https:'||u.protocol==='http:') && u.pathname.split('/').at(-1)===a.archive; } catch { return false; }\n});","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Point URLs directly at the artifact filename","Avoid redirect URLs whose last path segment differs"],"tags":["native-assets","manifest","url-validation"],"backgroundTag":"invalid-url-in-config","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}