fix(example): patch @expo/plist null-proto crash breaking iOS device install#63
Merged
Conversation
…install
`npm run mobile:ios:run:device` (and any `expo run:ios --device`) crashed at
the install step with:
TypeError: Cannot convert object to primitive value
at LockdowndClient.startSession (@expo/cli .../LockdowndClient.ts:119)
Root cause: @expo/plist 0.5.4 builds parsed plist objects with
`Object.create(null)` (null prototype). @expo/cli's device lockdownd handshake
does `debug(`startSession: ${pairRecord}`)`, and template-string coercion of a
null-prototype object throws "Cannot convert object to primitive value",
aborting before the app installs. @expo/cli itself is unchanged across 55.0.30
↔ 55.0.32 (byte-identical) — the regression is entirely in @expo/plist; other
projects only work because they happened to resolve 0.5.3.
npm `overrides` can't fix this here: @expo/cli pins @expo/plist to an exact
0.5.4 and npm refuses to override the nested copy (confirmed across many
attempts, including nested-path override syntax). So patch the one line via
patch-package instead:
- Object.create(null) → {} (build/parse.js, parsePlistXML dict branch)
This is JS tooling only — it runs on the dev's Mac during `expo run:ios`'s
install step. It does NOT touch Pods, `ios/`, or anything Expo CNG manages, so
it's fully CNG-compatible (we never `pod install`).
Adds patch-package as a devDep + `postinstall` hook so the patch auto-applies
on every install / clean clone. Verified: a fresh `npm ci` reports
"@expo/plist@0.5.4 ✔" and the device build now reaches install/launch.
Remove once @expo/plist restores Object.prototype or @expo/cli stops
string-coercing the pair record.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI installs the library only (`npm ci --workspaces=false`), so the example-app
dep @expo/plist isn't in the tree — and patch-package hard-fails on a patch
whose target package is missing ("Patch file found for package plist which is
not present"), breaking the CI install step.
Guard the hook so patch-package runs only when @expo/plist is actually
installed. When present (dev Mac, example installs) it still runs and still
fails loudly on a genuinely broken patch; when absent (CI lib-only) it skips
with exit 0. An `if [ -d ... ]` guard preserves real-failure visibility,
unlike `|| true` which would mask it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
npm run mobile:ios:run:devicecrashed at the install step:The app built fine every time — only the device install failed, so
expo run:ios --devicenever completed.Root cause
@expo/plist0.5.4 builds parsed plist objects withObject.create(null)(null prototype):@expo/cli's lockdownd handshake doesdebug(\startSession: ${pairRecord}`). The${pairRecord}` template coercion of a null-prototype object throws "Cannot convert object to primitive value", aborting before install.Confirmed it is not an
@expo/cliregression —LockdowndClient.jsis byte-identical between 55.0.30 and 55.0.32. Other local projects work only because they resolved@expo/plist0.5.3. Not iOS-version-related, not device-related, not our code.Why patch-package (not overrides)
npm
overridescan't fix it here:@expo/clipins@expo/plistto an exact 0.5.4 and npm refuses to override the nested copy (verified across many attempts, including nested-path override syntax + full lockfile regen — the latter also drifted 78 unrelated deps incl. worklets, breaking the native build).patch-packageedits the resolved file directly and replays on every install, version-pin-agnostic.The fix
patches/@expo+plist+0.5.4.patch— one line:Object.create(null)→{}patch-packagedevDep +postinstallhook → auto-applies on every install / fresh cloneCNG-safe
This is JS tooling only — it runs on the dev's Mac during
expo run:ios's install step. It does not touch Pods,ios/, or anything Expo CNG manages. We neverpod install. ✅Verification
npm cifrom clean →postinstallreports@expo/plist@0.5.4 ✔npm run mobile:ios:run:device→ Build Succeeded → Installing → app launches on device (previously crashed pre-install)Cleanup later
Remove once
@expo/plistrestoresObject.prototypeor@expo/clistops string-coercing the pair record.🤖 Generated with Claude Code