{"record":{"id":"fb7c107e84cac212","repo":"denoland/deno","slug":"cabinet-file-name-missing","errorCode":null,"errorMessage":"cabinet file {name} missing","messagePattern":"cabinet file (.+?) missing","errorType":"exception","errorClass":"AnyError","httpStatus":null,"severity":"error","filePath":"cli/tools/desktop.rs","lineNumber":5452,"sourceCode":"      folder.add_file(f.key.clone());\n    }\n  }\n\n  let cursor = std::io::Cursor::new(Vec::<u8>::new());\n  let mut writer = builder\n    .build(cursor)\n    .context(\"failed to start cabinet for .msi\")?;\n  // Files come back in the order they were added; read each one's bytes by its\n  // cab name (the MSI File key) to stay robust to ordering.\n  let by_key: std::collections::HashMap<&str, &MsiFile> =\n    files.iter().map(|f| (f.key.as_str(), f)).collect();\n  while let Some(mut file_writer) = writer\n    .next_file()\n    .context(\"failed to advance cabinet writer\")?\n  {\n    let name = file_writer.file_name().to_string();\n    let f = by_key.get(name.as_str()).ok_or_else(|| {\n      deno_core::anyhow::anyhow!(\"cabinet file {name} missing\")\n    })?;\n    let data = std::fs::read(&f.abs_path).with_context(|| {\n      format!(\"failed to read {} for .msi cabinet\", f.abs_path.display())\n    })?;\n    file_writer.write_all(&data)?;\n  }\n  let cursor = writer.finish().context(\"failed to finish cabinet\")?;\n  Ok(cursor.into_inner())\n}\n\n/// Recursively copy a directory tree, ensuring writable permissions on the\n/// destination. This is needed because source files from the Nix store are\n/// read-only.\n/// Strip unnecessary files from a CEF-based app bundle to reduce size.\n///\n/// Removes:\n/// - Non-English locale packs (~47MB)\n/// - SwiftShader software Vulkan renderer (~16MB, not needed on macOS with Metal)","sourceCodeStart":5434,"sourceCodeEnd":5470,"githubUrl":"https://github.com/denoland/deno/blob/f7822238cab635a3a19f99f493f675fa81a7f9d8/cli/tools/desktop.rs#L5434-L5470","documentation":"The Windows installer embeds files in a cabinet (.cab) stream: files are added to the cabinet writer up front, and when the writer yields each entry by name the code looks the bytes up by MSI File-table key (by_key map). This error means the writer produced an entry whose name has no corresponding MsiFile — the cabinet's name stream and the File-table keys disagree.","triggerScenarios":"A file was added to the cabinet under a name/cab-name that does not equal its MSI File key: duplicate keys in the file list (two entries collapse in the HashMap while the writer emits both), an empty or malformed key, or encoding/normalization differences between the cab name and the key string.","commonSituations":"The packaged file list accidentally contains two entries with the same key (e.g. same app name added for two architectures); a key contains characters the cabinet encoder transforms (case folding, path separators, non-ASCII); a future code path adds a cab entry without registering its MsiFile.","solutions":["Dump the file list feeding the .msi build and check for duplicate or empty MSI File keys; dedupe them at the source.","Verify each key round-trips through the cabinet writer's name encoding (ASCII, no path separators) before adding.","If keys are correct, log both the emitted cab name and the known keys at build time to spot the normalization difference, then normalize keys to cab names.","Report with the exact name from the error — it pinpoints which entry diverged."],"exampleFix":"// before: duplicates collapse in the map, writer still emits both\nlet by_key: HashMap<&str, &MsiFile> = files.iter().map(|f| (f.key.as_str(), f)).collect();\n// files contains key=\"app.dll\" twice -> one map entry, two cab entries -> \"cabinet file app.dll missing\"\n\n// after: dedupe/validate keys up front\nlet mut seen = std::collections::HashSet::new();\nfiles.retain(|f| !f.key.is_empty() && seen.insert(f.key.clone()));","handlingStrategy":"validation","validationCode":"// Validate the file list before building the cabinet: keys must be unique and non-empty\nfn valid_file_list(files: &[MsiFile]) -> Result<(), String> {\n    let mut seen = std::collections::HashSet::new();\n    for f in files {\n        if f.key.is_empty() { return Err(format!(\"empty MSI key for {}\", f.abs_path.display())); }\n        if !seen.insert(f.key.clone()) { return Err(format!(\"duplicate MSI key: {}\", f.key)); }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"Catch the error, log the missing name from the message together with the full key set, and fail the build — the name in the error identifies exactly which cab entry lacks a matching MsiFile, making the divergent producer easy to fix.","preventionTips":["Deduplicate and validate MSI File keys where the file list is assembled, not inside the cabinet writer loop.","Keep keys ASCII, path-separator-free, and identical to the cab names you register.","When adding a new file to the installer, always add both the cab entry and its MsiFile in the same change."],"tags":["windows","msi","cabinet","installer","build"],"backgroundTag":"missing-build-input-file","analyzedSha":"f7822238cab635a3a19f99f493f675fa81a7f9d8","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}