{"record":{"id":"ec16d87ff6f3768b","repo":"denoland/deno","slug":"malformed-msi-no-stringpool-stream","errorCode":null,"errorMessage":"malformed .msi: no _StringPool stream","messagePattern":"malformed \\.msi: no _StringPool stream","errorType":"exception","errorClass":"AnyError","httpStatus":null,"severity":"error","filePath":"cli/tools/desktop.rs","lineNumber":5291,"sourceCode":"  let write_stream = |comp: &mut cfb::CompoundFile<std::fs::File>,\n                      raw: &str,\n                      bytes: &[u8]|\n   -> Result<(), AnyError> {\n    let mut s = comp.open_stream(format!(\"/{raw}\"))?;\n    s.write_all(bytes)?;\n    Ok(())\n  };\n  let find_raw = |suffix: &str| -> Option<String> {\n    names\n      .iter()\n      .find(|n| msi_demangle_stream_name(n).ends_with(suffix))\n      .cloned()\n  };\n\n  // The string-pool header's top bit selects 3-byte string references; tiny\n  // databases use 2-byte refs, but honor the flag to stay correct at any size.\n  let pool_raw = find_raw(\"_StringPool\").ok_or_else(|| {\n    deno_core::anyhow::anyhow!(\"malformed .msi: no _StringPool stream\")\n  })?;\n  let pool = read_stream(&mut comp, &pool_raw)?;\n  let str_w: usize = if pool.len() >= 4 && (pool[3] & 0x80) != 0 {\n    3\n  } else {\n    2\n  };\n\n  // Map every string-pool id to its text so data-table streams (named by table\n  // text) can be matched to their column schema (keyed by table string id).\n  let data_raw = find_raw(\"_StringData\").ok_or_else(|| {\n    deno_core::anyhow::anyhow!(\"malformed .msi: no _StringData stream\")\n  })?;\n  let str_data = read_stream(&mut comp, &data_raw)?;\n  let mut strings = vec![String::new()]; // 1-based; index 0 is unused.\n  {\n    let mut off = 0usize;\n    let mut i = 4;","sourceCodeStart":5273,"sourceCodeEnd":5309,"githubUrl":"https://github.com/denoland/deno/blob/f7822238cab635a3a19f99f493f675fa81a7f9d8/cli/tools/desktop.rs#L5273-L5309","documentation":"deno desktop's Windows installer repacks an existing .msi by directly editing its OLE compound-file streams (after de-mangling stream names). _StringPool is the MSI string-table header stream every valid MSI must contain; its absence means the input is not a well-formed MSI database — wrong file type, truncated template, or stream names mangled beyond what msi_demangle_stream_name can undo.","triggerScenarios":"find_raw(\"_StringPool\") finds no stream whose de-mangled name ends with _StringPool: passing a non-MSI file as the installer template, a corrupted/truncated .msi, or an .msi produced by a tool that names/encodes streams in an unexpected way.","commonSituations":"The bundled installer template was replaced or corrupted in a fork; a download of the template was truncated; the file is actually an .exe bootstrapper rather than the .msi database; a repack pipeline fed the output of a previous failed run back in as input.","solutions":["Verify the input is a real MSI database: `file template.msi` (should say 'Composite Document File') and open it with lessmsi or msidb to confirm streams exist.","Re-obtain the pristine .msi template (re-download/rebuild) — a valid MSI always has _StringPool and _StringData.","If the template is custom-built, generate it with a standard tool (WiX) so the system-table streams exist.","Check that no earlier repack step already rewrote stream names into a form the demangler cannot reverse."],"exampleFix":"# before: wrong file handed to the repack\nfile installer/template.msi\n# PE32 executable (GUI)  -> it is a bootstrapper .exe, not an .msi\n\n# after: supply the real database\nfile installer/template.msi\n# Composite Document File V2 Document  -> OK, re-run the build","handlingStrategy":"validation","validationCode":"# Validate the template is a real MSI with the expected system streams before repacking\nfile template.msi | grep -q 'Composite Document File' || { echo 'not an OLE/MSI file'; exit 1; }\n7z l template.msi | grep -q '_StringPool' || { echo 'no _StringPool stream'; exit 1; }","typeGuard":null,"tryCatchPattern":"Catch the 'malformed .msi' family, re-check the file signature out-of-band, and fail the installer build with 'template is not a valid MSI' — retrying the same input is pointless since the file on disk will not change.","preventionTips":["Fetch installer templates from a checksum-pinned source and verify size/digest before use.","Never feed bootstrapper .exe files or previously repacked outputs where a database .msi is expected.","Keep the deno desktop repack pipeline single-pass: pristine template in, installer out."],"tags":["windows","msi","installer","file-format","validation"],"backgroundTag":"msi-validation-failed","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"}