{"record":{"id":"7cf38ef468bbe5f1","repo":"denoland/deno","slug":"helper-plist-has-no-cfbundleidentifier","errorCode":null,"errorMessage":"helper plist {} has no CFBundleIdentifier","messagePattern":"helper plist (.+?) has no CFBundleIdentifier","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/tools/desktop.rs","lineNumber":2552,"sourceCode":"      || format!(\"failed to rewrite helper plist at {}\", plist_path.display()),\n    )?;\n  }\n  Ok(())\n}\n\n/// Rewrite a single helper's `CFBundleIdentifier` based on the existing\n/// value's \"kind\" suffix.\nfn rewrite_helper_plist_identifier(\n  plist_path: &Path,\n  main_bundle_id: &str,\n) -> Result<(), AnyError> {\n  let mut dict: plist::Dictionary = plist::from_file(plist_path)\n    .with_context(|| format!(\"failed to parse {}\", plist_path.display()))?;\n  let existing = dict\n    .get(\"CFBundleIdentifier\")\n    .and_then(|v| v.as_string())\n    .ok_or_else(|| {\n      deno_core::anyhow::anyhow!(\n        \"helper plist {} has no CFBundleIdentifier\",\n        plist_path.display()\n      )\n    })?;\n  // Extract the suffix from the last `helper` segment onward. Falls back\n  // to a bare `helper` if the existing id doesn't contain that token\n  // (defensive — every laufey helper plist has it today).\n  let suffix = existing\n    .find(\"helper\")\n    .map(|i| &existing[i..])\n    .unwrap_or(\"helper\");\n  let new_id = format!(\"{main_bundle_id}.{suffix}\");\n  dict.insert(\n    \"CFBundleIdentifier\".to_string(),\n    plist::Value::String(new_id),\n  );\n  // Write XML format for stability and human-diffability. Helper plists\n  // are tiny so we don't gain anything by switching to binary plist","sourceCodeStart":2534,"sourceCodeEnd":2570,"githubUrl":"https://github.com/denoland/deno/blob/f7822238cab635a3a19f99f493f675fa81a7f9d8/cli/tools/desktop.rs#L2534-L2570","documentation":"While bundling a macOS app, deno rewrites each helper's Info.plist CFBundleIdentifier so helper ids nest under the main bundle id (needed for nested-helper launching and code signing). This error means a helper Info.plist parsed successfully but has no CFBundleIdentifier key, or the value is not a string, so there is no 'kind' suffix to preserve.","triggerScenarios":"rewrite_helper_plist_identifier() parses a helper plist from the laufey-provided .app where CFBundleIdentifier is missing or of the wrong type — e.g. a hand-edited/corrupted plist in a dev checkout, or a laufey release that changed helper plist shape.","commonSituations":"A developer edited a helper Info.plist (e.g. experimenting with ids) and dropped the key; a corrupted cache extraction left a truncated plist; upstream laufey renamed the key for some helper.","solutions":["Open the plist named in the error (plutil -p <path>) and confirm CFBundleIdentifier is present and a string.","If it came from the download cache, delete that cache dir and re-run to get a pristine, checksum-verified copy.","If you edited it intentionally, restore a string CFBundleIdentifier (the code derives the new id from the existing value's 'helper' suffix, so any sensible id like com.deno.desktop.helper works).","On a dev checkout (LAUFEY_DEV_DIR), rebuild/re-checkout laufey so the helper plists are regenerated."],"exampleFix":"# before: helper plist lost its id\nplutil -p MyApp.app/Contents/Frameworks/laufey.helper.app/Info.plist\n# { \"CFBundleExecutable\": \"laufey.helper\", ... }   # no CFBundleIdentifier\n\n# after: restore it, or refetch pristine\ndefaults write <plist> CFBundleIdentifier com.deno.desktop.helper   # or:\nrm -rf ~/.cache/deno/desktop/laufey && deno desktop","handlingStrategy":"validation","validationCode":"# Validate helper plists before bundling: every one needs a string CFBundleIdentifier\nfor p in MyApp.app/Contents/Frameworks/*.app/Info.plist; do\n  plutil -extract CFBundleIdentifier raw \"$p\" >/dev/null 2>&1 \\\n    || { echo \"missing CFBundleIdentifier in $p\"; exit 1; }\ndone","typeGuard":null,"tryCatchPattern":"Catch the error, name the offending plist path from the message, and fail the bundle step with that path — silently skipping the rewrite would break nested-helper launching and signing downstream, so do not swallow it.","preventionTips":["Never edit helper Info.plists without keeping CFBundleIdentifier a string.","Regenerate hand-modified .app bundles from source instead of patching them in place.","Keep the laufey cache pristine; if plists look odd, clear the cache and re-verify via checksum."],"tags":["macos","plist","code-signing","app-bundle","desktop"],"backgroundTag":"plist-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"}