# Troubleshooting

Fixes for the problems people run into most, listed by what you see. Start with squiggle doctor: it checks the simulator, your app, screen tracking, source lines and your licence, and names the fix for each problem it finds.

```shell
squiggle doctor
```

## Install and set up

### squiggle: command not found

If you installed Studio, install the command from it: open Settings (`⌘ ,`) and press Install next to Command line. Studio links `squiggle` into `~/.local/bin`, and app updates update it too. If your shell still can’t find it, add `~/.local/bin` to your `PATH`.

### Metro can’t find a package in a monorepo

If your app is in a workspace and its dependencies are installed at the repository root, Metro only looks in the app’s own folder. Let it look at the root too, and keep `withSquiggle` around the config you export:

```ts
// metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const { withSquiggle } = require('@squiggle.sh/react-native/metro');
const path = require('path');

const projectRoot = __dirname;
const workspaceRoot = path.resolve(projectRoot, '../..');

const config = getDefaultConfig(projectRoot);
config.watchFolders = [workspaceRoot];
config.resolver.nodeModulesPaths = [
  path.resolve(projectRoot, 'node_modules'),
  path.resolve(workspaceRoot, 'node_modules'),
];

module.exports = withSquiggle(config);
```

### pod install fails with Yarn 1

The SDK needs `react-native-nitro-modules` 0.35. Other package managers install it with the SDK; Yarn 1 only prints a warning. Add it yourself, then run `pod install` again:

```shell
yarn add react-native-nitro-modules@^0.35.9
```

## Running flows

### No booted simulator, or the device is busy

`no booted simulator found` means no simulator is running. Boot one in Xcode or Studio, or pass `--udid` to choose one when several are running.

Only one run can use a simulator at a time. A second run stops with exit code 5:

```shell
squiggle flow checkout
```

Output:

```text
another squiggle run holds this device (pid 1234, started 40 s ago)
```

Wait for the first run to finish, or run the second on another simulator.

### Port 7777 is held by another program

`:7777 is held by another Squiggle producer, not Studio` names the process that holds the port. Quit it and run again; relaunching the app doesn’t free the port. If the message says Studio may be wedged, quit and reopen Studio.

If you can’t free the port, use another one. Set `"port": 7790` in `.squiggle/config.jsonc`, or `SQUIGGLE_PORT=7790` for one command, and point your app at the same port in its `.env`:

```shell
EXPO_PUBLIC_SQUIGGLE_WS_URL=ws://localhost:7790
```

Restart Metro with `--clear` so the app picks it up.

### A command drives Studio or another app instead of your simulator

With no `--udid`, `squiggle save`, `squiggle load` and your agent’s tools drive the app that Squiggle launched most recently. If that was Studio or another simulator, pass `--udid` with the simulator you mean, or run `squiggle up` for your app again so it is the newest. A flow, and anything it runs beside itself such as `--tune` or `--bisect-state`, always drives the booted simulator for its platform, or the one `--udid` names.

### Runs are slow or flaky, or typing fails

Your app was probably launched without Squiggle attached, for example from Xcode or with `expo run:ios` on its own. Flows still run, but each run takes a few seconds longer. A step that waits for the app to settle can’t ask the app, so it moves on after a few unanswered checks, and the next step can act in the middle of an animation. Typing goes through the simulator’s keyboard instead, and a `type` step can fail with `never took keyboard focus over HID` or, on a busy Mac, `keystrokes were dropped`. `doctor` shows a warning on its `app injected` line.

Relaunch the app with Squiggle attached:

```shell
squiggle up
```

To stop it happening after every rebuild, end your `ios` script with `&& squiggle up`. `squiggle init` does this for the two common scripts. Studio always launches the app with Squiggle attached.

### A step waits a long time for the app to settle

Something on screen is still animating. Run the flow with `--settle-debug` to log every settle check. When a wait runs out, it names what kept it waiting: the component and its `file:line`, when Squiggle can find them, and the animated property.

```shell
squiggle flow checkout --settle-debug
```

### Every step suddenly takes seconds after an update

The app is still running the copy of Squiggle it was launched with, from before you updated. `doctor` reports that the running copy doesn’t match this build, and a run can stop with `E_STALE_DYLIB`. Quit the app completely, then relaunch it:

```shell
xcrun simctl terminate booted <bundle-id>
squiggle up
```

### A step passes, but nothing happened in the app

A passing tap means the tap reached the control, not that the app reacted. If nothing changed afterwards, the step shows the warning `no graph reaction`. Add an `assert` after the action to check the result. See [How Squiggle works](https://squiggle.sh/docs/how-it-works#a-passing-step-was-sent-not-proven).

### The second run fails at the first step

A flow starts from wherever the last run left the app. If the last run failed halfway, or ended on a different screen, the next one can’t find its first control, and the error often looks like a broken selector. Relaunch the app before the run:

```shell
squiggle flow login --fresh
```

To avoid this for good, give the flow a starting state (see [flows](https://squiggle.sh/docs/flows)).

### A tap fails because its target is off screen

Squiggle won’t tap a control that is outside the screen, because the tap would land on whatever is at the edge instead. The error names the control’s position and the screen size. Scroll to it first with a `scrollUntilVisible` step, or run with `--auto-scroll` to scroll automatically.

### A flow won’t load: not valid YAML

The flow file isn’t valid YAML. The run stops with exit code 2 and `E_FLOW_PARSE`, and names the line and column:

```shell
squiggle flow orders
```

Output:

```text
flow: not valid YAML — Flow map in block collection must be sufficiently indented and end with a } at line 6, column 1:

      visible: { text: Order #20 }
```

The usual cause is a `#` in a value: YAML reads everything from ` #` on as a comment. Inside `{ }` that swallows the closing brace, which is the error above. On a line of its own it isn’t an error at all: `text: Order #20` looks for `Order`. Quote any value with a `#` in it:

```shell
- assert:
    visible: { text: "Order #20" }
```

### A step passes normally but fails with --ax-only

The step fails with `ax_unreachable`: no element in the accessibility tree carries the selector, or several announce it the same way, so a VoiceOver user can’t reach the control. Give it an `accessibilityLabel` or a `testID` that names it uniquely. See [Run settings](https://squiggle.sh/docs/run/settings).

### A flow passes on its own but fails on the report card

The report card runs each flow offline, against stubs built from its last green run. A stub keeps each response’s shape but not its values: strings read like `title 1` and numbers count up from 1. A flow that asserts text from your server, such as a product name, fails there. Run the card with `--live` to use your real backend, or assert on something the server doesn’t write, such as a `testID`.

### The report card’s startup row stays pending

The row says `which screens load at startup was not measured`. Squiggle times the modules your app evaluates at startup only when your Metro config is wrapped in `withSquiggle` (see [withSquiggle](https://squiggle.sh/docs/reference/sdk#withsquiggle)) and the bundle was built with it. Add it, restart Metro with `--clear`, and run the card again. Screens are read from Expo Router’s `app/` route files, so an app without them always shows the row as pending.

### The report card’s cliff row stays pending

The card reads the newest `--cliff` sweep and never runs one. The row stays pending until a sweep reaches the size the screen declares, and again whenever the screen’s route file, or the file of the component the sweep named, changes. Run the sweep the row’s fix names, with a range that goes past `max`. See [Keep a screen off its cliff](https://squiggle.sh/docs/performance#keep-a-screen-off-its-cliff).

### A report card row says the app was not running

The row reads `no app on … connected … and this run does not relaunch it`. The app crashed or exited during an earlier flow, and this flow waited 30 seconds for it before giving up. Any run that doesn’t relaunch the app itself, such as `squiggle flows` or a benchmark, stops the same way. A crash leaves a report in `~/Library/Logs/DiagnosticReports`. The card starts the app again for the next flow, as it does after any failed flow. When you run one flow by itself, relaunch the app with `squiggle up`, or run the flow with `--fresh`.

### The app reloads in the middle of a run

Two Metro servers are serving the same app, usually because `expo start` is running twice. The app loads the bundle from one and then reloads from the other. Stop every Metro server except one.

### React Native DevTools disconnects during a run

Squiggle keeps the app’s debugger connection for the run, to capture a JavaScript profile when the JS thread stalls, and Metro allows one debugger per app, so DevTools disconnects until the run ends. Reconnect afterwards, or turn the capture off. See [Profiling](https://squiggle.sh/docs/performance/profiling).

## Saves and state

### squiggle load refuses a save

A save is checked against your app before anything is restored. If a store or screen it holds has changed or gone, `squiggle load` refuses the whole save, names what changed, and leaves your app as it was. A save taken before your stores had names, which lists stores such as `store:zustand-2`, is refused the same way once they do. Save it again. See [Save and restore your app’s state](https://squiggle.sh/docs/reference/sdk#save-and-restore-your-apprsquos-state).

### squiggle load fails on another simulator

MMKV data is copied on the simulator that took the save, so it loads only there. On another simulator, or after you reset the app’s data, the screen and stores are restored but every MMKV instance is left as it was, and the load reports failure naming the instance. Take the save again on this simulator.

### Stores show as store:zustand-3 after an upgrade

Metro is serving a bundle built before the Babel plugin could name your stores. Restart it with `--clear`:

```shell
npx expo start --clear
```

A store created inside a component keeps a numbered name. See [Store names](https://squiggle.sh/docs/reference/sdk#store-names).

### squiggle product state is empty

The app sends no state samples. That also leaves `--where` and `friction --state` with nothing to split by. Build the app with `EXPO_PUBLIC_SQUIGGLE_STATE_ANALYTICS=1`, debug builds included, and check that your app doesn’t pass `false` to `setStateAnalyticsConsent`. See [Turn on state samples](https://squiggle.sh/docs/reference/sdk#turn-on-state-samples).

## Studio and the Inspector

### Screens show as “(no screen)”

Squiggle can’t see which screen is showing, so waiting for a screen, checking it and the Screen map don’t work. `doctor` warns on its `route tracking` line. Wrap your root in `<Squiggle navigationRef={…}>`, as in [Set up your app](https://squiggle.sh/docs/setup).

### The Inspector can’t show a component’s file and line

The Babel plugin isn’t in your build. `doctor` warns on its `source locations` line. Add `@squiggle.sh/babel-plugin` to `babel.config.js` (`squiggle init` does it), then rebuild the app. A reload isn’t enough.

### The on-device overlay doesn’t appear

The overlay only draws in a development build, and only when the app isn’t connected to Studio or the CLI. When Studio or a flow launches the app, the overlay stays hidden, because Studio has its own Inspector. To use the overlay on its own, add `EXPO_PUBLIC_SQUIGGLE_WS_URL=off` to your app’s `.env` and launch the app yourself.

## Still stuck

Run `squiggle doctor --bundle`. It writes a support file on your Mac with versions, settings and paths relative to your project, and none of your code. You can read it before you send it. Email it to [support@squiggle.sh](mailto:support@squiggle.sh?subject=Squiggle%20bug%20report&body=Hi%20%E2%80%94%20I%20think%20I%20found%20a%20bug.%0A%0AWhat%20I%20did%3A%0AWhat%20I%20expected%3A%0AWhat%20happened%20instead%3A%0AOutput%20of%20%60squiggle%20doctor%60%3A%0A) with what you did and what happened.
