{"record":{"id":"d0b84c4b1e822233","repo":"Yeachan-Heo/oh-my-codex","slug":"manifest-archive-invalid","errorCode":"manifest_archive_invalid","errorMessage":"[native-assets] manifest_archive_invalid: ${asset.archive}","messagePattern":"\\[native-assets\\] manifest_archive_invalid: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/native-assets/policy.ts","lineNumber":174,"sourceCode":"      throw policyError('manifest_target_mismatch', asset.target || asset.archive);\n    }\n    const expectedBinary = nativeProductBinaryName(asset.product);\n    const expectedBinaryPath = nativeProductBinaryPath(asset.product, asset.platform);\n    if (!expectedBinary || !expectedBinaryPath || asset.binary !== expectedBinary) {\n      throw policyError('manifest_binary_product_mismatch', asset.binary);\n    }\n\n    const binaryPath = normalizeNativeArchivePath(asset.binary_path, 'file');\n    if (binaryPath !== asset.binary_path || binaryPath.split('/').at(-1) !== expectedBinaryPath) {\n      throw policyError('manifest_binary_basename_mismatch', asset.binary_path);\n    }\n\n    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":156,"sourceCodeEnd":191,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/native-assets/policy.ts#L156-L191","documentation":"The asset's archive field must equal its canonical basename (from nativeReleaseAssetBasename) and end with a recognized archive suffix (checked by nativeArchiveSuffix). This prevents directory components and unsupported archive formats.","triggerScenarios":"asset.archive contains a path ('dist/foo.tar.zst'), has no recognized extension, or disagrees with the basename reconstructed from the asset's fields.","commonSituations":"Using .tar.gz when only .tar.zst is supported, or leaking a full relative path into the archive name field.","solutions":["Set asset.archive to the exact basename with a supported suffix (e.g. .tar.zst)","Verify nativeReleaseAssetBasename(asset) reproduces the same string","Avoid directory separators in the archive field"],"exampleFix":"// before\n\"archive\": \"dist/omxd-linux-x64.tar.gz\"\n// after\n\"archive\": \"omxd-linux-x64.tar.zst\"","handlingStrategy":"validation","validationCode":"const ok = assets.every((a) => a.archive === nativeReleaseAssetBasename(a) && !!nativeArchiveSuffix(a.archive));","typeGuard":"function hasValidArchive(a: NativeAsset): boolean {\n  return a.archive === nativeReleaseAssetBasename(a) && !!nativeArchiveSuffix(a.archive);\n}","tryCatchPattern":null,"preventionTips":["Keep archive fields to bare filenames with supported suffixes","Check nativeArchiveSuffix's supported list before renaming archives"],"tags":["native-assets","manifest","archive-format"],"backgroundTag":"manifest-validation-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}