stablyai/orca · error · Error

pnpm install completed, but node_modules/.bin/expo is still

Error message

pnpm install completed, but node_modules/.bin/expo is still missing.

What it means

Thrown by the mobile Expo CLI launcher after it auto-runs `pnpm install --frozen-lockfile` in the mobile directory and still cannot locate node_modules/.bin/expo. The script first checks for expo; if missing it installs deps; if expo is STILL missing post-install, it treats the dependency state as broken rather than launching a missing binary.

Source

Thrown at mobile/scripts/mobile-expo-cli.mjs:53

  })
}

export async function ensureMobileExpoCli(mobileDir, logger = {}) {
  if (getMobileExpoExecutablePath(mobileDir)) {
    return
  }

  const message = 'Mobile dependencies are missing; running pnpm install --frozen-lockfile...'
  if (logger.logStep) {
    logger.logStep('deps', message)
  } else {
    console.log(`[start] ${message}`)
  }

  await runPnpmInstall(mobileDir)

  if (!getMobileExpoExecutablePath(mobileDir)) {
    throw new Error('pnpm install completed, but node_modules/.bin/expo is still missing.')
  }

  logger.logSuccess?.('Mobile dependencies installed')
}

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Verify `expo` is present in mobile/package.json dependencies/devDependencies and run `pnpm install` (without --frozen-lockfile) to regenerate the lockfile if needed.
  2. Delete mobile/node_modules and mobile/pnpm-lock.yaml's effect by removing node_modules, then reinstall.
  3. Check getMobileExpoExecutablePath() handles the current OS (look for expo vs expo.cmd vs expo.ps1).
  4. Confirm pnpm is the intended manager and the workspace root's pnpm-workspace.yaml includes mobile.

Example fix

// before
await runPnpmInstall(mobileDir)
if (!getMobileExpoExecutablePath(mobileDir)) {
  throw new Error('pnpm install completed, but node_modules/.bin/expo is still missing.')
}

// after — capture install exit code and report it
const installResult = await runPnpmInstall(mobileDir)
if (!getMobileExpoExecutablePath(mobileDir)) {
  throw new Error(`pnpm install (exit ${installResult.code}) completed, but node_modules/.bin/expo is still missing. Check that 'expo' is in package.json.`)
}
Defensive patterns

Strategy: validation

Validate before calling

// Verify expo is declared before attempting install
import { readFileSync } from 'node:json'
const pkg = JSON.parse(readFileSync(join(mobileDir, 'package.json'), 'utf8'))
const hasExpo = Boolean((pkg.dependencies?.expo) || (pkg.devDependencies?.['expo-cli']))
if (!hasExpo) {
  throw new Error("'expo' is not in mobile/package.json; add it before launching")
}

Type guard

function expoExecutableExists(mobileDir: string): boolean {
  return Boolean(getMobileExpoExecutablePath(mobileDir))
}

Try / catch

try {
  await runPnpmInstall(mobileDir)
} catch (installErr) {
  throw new Error(`pnpm install failed: ${(installErr as Error).message}`)
}
if (!getMobileExpoExecutablePath(mobileDir)) {
  throw new Error('pnpm install completed, but node_modules/.bin/expo is still missing.')
}

Prevention

When it happens

Trigger: mobile/node_modules/.bin/expo is absent after pnpm install — e.g. expo is not declared in package.json, the lockfile is out of sync and --frozen-lockfile exits but the script did not catch it, pnpm hoisted expo elsewhere, or the platform-specific expo binary shim failed to link.

Common situations: A package.json change that removed expo-cli/expo without updating the lockfile, a corrupted node_modules from a previous interrupted install, a pnpm version mismatch that lays out .bin differently, or running on Windows where .bin/expo is expo.cmd and the path check looks for the wrong suffix.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/5e0a958fd08846c7. Report an issue: GitHub.