expo/expo · critical
Dylib not found at ${binaryPath}. Build it first by running:
Error message
Dylib not found at ${binaryPath}. Build it first by running: cd ${path.dirname(path.join(frameworkPath, '..'))} && ./scripts/build.sh What it means
Thrown by getDylibPath() when the compiled IOSScreenInspectorFramework binary is not present at the expected path inside the e2e/image-comparison/inspector/bin directory. This framework is an out-of-tree native dylib that the iOS simulator screen inspector injects into the app via DYLD_INSERT_LIBRARIES. The error is a hard precondition gate: nothing in ScreenInspectorIOS can work until the binary is built.
Source
Thrown at apps/bare-expo/e2e/image-comparison/inspector/ScreenInspectorIOS.ts:47
bounds?: {
x: number;
y: number;
width: number;
height: number;
};
path?: string;
width?: number;
height?: number;
error: string;
}
export function getDylibPath(): string {
const frameworkName = 'IOSScreenInspectorFramework.framework';
const frameworkPath = path.resolve(__dirname, 'bin', frameworkName);
const binaryPath = path.join(frameworkPath, 'IOSScreenInspectorFramework');
if (!fs.existsSync(binaryPath)) {
throw new Error(
`Dylib not found at ${binaryPath}. Build it first by running: cd ${path.dirname(path.join(frameworkPath, '..'))} && ./scripts/build.sh`
);
}
return binaryPath;
}
export class ScreenInspectorIOS {
private requestPipePath = '/tmp/ios_screen_inspector_request';
private responsePipePath = '/tmp/ios_screen_inspector_response';
async getCoordinates(
accessibilityId: string,
timeoutMs: number = 15000
): Promise<{
x: number;
y: number;
width: number;View on GitHub (pinned to b09195aac2)
Solutions
- Run the exact build command the message prints: cd into path.dirname of the parent of bin (the inspector package root) and execute ./scripts/build.sh to compile the framework.
- Confirm the binary now exists at the path printed in the error (the IOSScreenInspectorFramework executable inside bin/IOSScreenInspectorFramework.framework), not just the .framework directory.
- If build.sh fails, run it directly and read its output — it typically requires Xcode command line tools and a valid iOS SDK; ensure xcode-select points at the active developer dir.
- On non-macOS hosts, these tests are unsupported — gate them behind a darwin platform check rather than letting getDylibPath throw.
Example fix
// before: tests run on any platform and throw at getDylibPath()
if (process.platform !== 'darwin') {
throw new Error('iOS screen inspector e2e requires macOS');
}
// after: ensure the framework is built before constructing the inspector
import { execSync } from 'child_process';
import { existsSync } from 'fs';
import path from 'path';
const frameworkBinary = path.resolve(__dirname, 'bin', 'IOSScreenInspectorFramework.framework', 'IOSScreenInspectorFramework');
if (!existsSync(frameworkBinary)) {
execSync('./scripts/build.sh', { stdio: 'inherit', cwd: path.resolve(__dirname, '..') });
} Defensive patterns
Strategy: validation
Validate before calling
import { existsSync } from 'fs';
import path from 'path';
const frameworkBinary = path.resolve(__dirname, 'bin', 'IOSScreenInspectorFramework.framework', 'IOSScreenInspectorFramework');
const isDylibBuilt = existsSync(frameworkBinary);
if (!isDylibBuilt) {
throw new Error(`Framework not built. Run ./scripts/build.sh in ${path.resolve(__dirname, '..')}`);
} Prevention
- Add a pretest npm script that runs scripts/build.sh so the framework exists before any e2e target invokes getDylibPath.
- Gate iOS e2e behind process.platform === 'darwin' to fail fast on unsupported hosts.
- Commit a CI step that verifies the binary exists immediately after the build step, failing the job there rather than deep in a test.
When it happens
Trigger: Calling getDylibPath() (directly, or transitively through ScreenInspectorIOS.getCoordinates / captureView / startSimulatorAppWithDylib) on a fresh checkout, after a clean, after switching branches that touch the framework, or on a CI runner where the build step was skipped or failed. The check is fs.existsSync(binaryPath) failing on the framework's executable file (not the .framework bundle).
Common situations: Running e2e image-comparison tests for the first time without running the build script; the framework was .gitignored so a clone lacks it; a previous ./scripts/build.sh failed silently; running on a non-macOS host where the framework cannot be compiled at all.
Related errors
- Failed to open pipe ${pipePath} for writing: ${error.message
- Failed to take iOS screenshot: ${error instanceof Error ? er
- Get coordinates failed: ${response.error}
- No bounds returned in response
- Capture view failed: ${response.error}
AI-assisted analysis of expo/expo@b09195aac2 (2026-08-12).
Data as JSON: /api/errors/6e0afcbfcd66c898.
Report an issue: GitHub.