> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mobileboost.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting local testing

> Diagnose tunnel problems, work with corporate proxies, and get the network changes you need

## Start with doctor

`mb-local doctor` checks every step of the path from your machine and names the
one that is broken. Run it before anything else.

```bash theme={null}
mb-local doctor --key $MOBILEBOOST_API_KEY --only-hosts 'staging.acme.internal:443'
```

```
mb-local doctor

  [PASS] environment                  mb-local 1.4.0 on darwin/arm64
  [PASS] egress path                  system proxy proxy.corp:8080 (scutil --proxy)
  [WARN] tls path                     intercepted by Zscaler (gateway)
  [PASS] control plane                https://api.mobileboost.io reachable (HTTP 200)
  [PASS] authentication               org acme, tunnel tun_4kd8xz2m9qwerty1
  [PASS] clock skew                   211ms from the server clock
  [PASS] relay https                  relay-eu.mobileboost.io reachable (HTTP 200)
  [FAIL] relay websocket              upgrade answered with HTTP 403 (expected 101)
  [PASS] target staging.acme.internal 10.4.2.19:443 reachable from this machine
```

Read the **first FAIL** and ignore the rest. Everything below it usually fails as
a consequence.

The output contains no keys, tokens, or traffic, so it is safe to paste into a
support ticket. Add `--json` for machine-readable output.

## Getting to MobileBoost

### The control plane is unreachable

```
[FAIL] control plane   https://api.mobileboost.io: dial tcp: i/o timeout
```

Your machine has no route out to MobileBoost. Ask for outbound TCP 443 to
`api.mobileboost.io` and `relay-eu.mobileboost.io`, or configure your corporate
proxy with `--proxy-host`.

### A corporate proxy is in the way

`mb-local` reads `HTTPS_PROXY` and `NO_PROXY`, and on macOS it reads system
settings too. The `egress path` line tells you what it found.

If it found nothing and you know there is a proxy:

```bash theme={null}
mb-local --proxy-host proxy.corp --proxy-port 8080
```

If it found one and you need to bypass it:

```bash theme={null}
mb-local --no-proxy-discovery
```

### The proxy asks for a password

```
[FAIL] relay websocket   the proxy demands authentication (407): Basic realm="corp"
```

Pass credentials:

```bash theme={null}
mb-local --proxy-host proxy.corp --proxy-port 8080 \
         --proxy-user "$PROXY_USER" --proxy-pass "$PROXY_PASS"
```

<Note>
  Kerberos and NTLM proxy authentication are not supported yet. If your proxy
  requires either, ask for these hosts to be allowed without authentication, or
  run the tunnel on a host that does not sit behind that proxy.
</Note>

### Your machine uses a PAC file

```
[FAIL] egress path   proxy auto-config (PAC) evaluation is not supported yet:
                     http://wpad.corp/wpad.dat
```

PAC files are detected but not yet evaluated. Open the PAC file, find the proxy
it selects for `mobileboost.io`, and pass it directly:

```bash theme={null}
mb-local --proxy-host proxy.corp --proxy-port 8080
```

### TLS inspection is rewriting certificates

```
[WARN] tls path   intercepted by Zscaler (gateway); certificate issued by
                  CN=Zscaler Intermediate Root CA
```

Your company decrypts and re-signs outbound TLS. This is not automatically a
problem, and a warning alone does not mean the tunnel is broken. It becomes a
problem when the inspection also blocks the tunnel's connection, which shows up
as the next failure.

If the certificate does not validate at all, install your corporate root CA in
the system trust store, or point at it directly:

```bash theme={null}
mb-local --ca-cert /path/to/corporate-root.pem
```

<Tip>
  `mb-local` also recognises local antivirus products that inspect TLS the same
  way. If the named product is running on your laptop rather than at the network
  edge, the fix is local: exclude MobileBoost from its HTTPS scanning.
</Tip>

### The connection is refused even though the host is reachable

```
[PASS] relay https       relay-eu.mobileboost.io reachable (HTTP 200)
[FAIL] relay websocket   upgrade answered with HTTP 403 (expected 101)
```

This pair is the clearest signal there is. Ordinary HTTPS to that exact host
works, so the host, the route, the proxy, and TLS are all fine. Something on the
path is specifically refusing the tunnel's connection.

This needs a change on your network, and `mb-local` writes the request for you:

```bash theme={null}
mb-local network-requirements --key $MOBILEBOOST_API_KEY
```

It prints a document that names the appliance it detected, the exact policy
feature to change in that product, what MobileBoost does **not** need, and the
evidence from your machine. Send it to whoever manages your network.

<Note>
  The usual resolution is a TLS inspection exception for the MobileBoost hosts.
  Every major gateway supports one, and it is a routine request: Zscaler calls it
  **Do Not Inspect**, Netskope **SSL Do Not Decrypt**, Palo Alto **No Decrypt**.
  The generated document uses the right term for the product it found.
</Note>

If an exception is not possible, run the tunnel somewhere that is not behind that
gateway. A small VM in a DMZ or a container next to your staging environment
works, and one shared tunnel there serves everyone. See
[Run a shared tunnel](/test-agent/local-testing-ci#run-a-shared-tunnel).

### The clock is wrong

```
[FAIL] clock skew   this machine is 11m4s away from the server clock
```

Credentials and TLS both fail in confusing ways when a clock drifts. Enable
network time sync. Laptops that have been suspended for a long time and CI images
with no NTP are the usual causes.

## Reaching your own services

### Requests are denied

Your `status` output shows a growing `denied` count, and your test fails to reach
a host you expected to work.

```
traffic     42 dials, 0 errors, 17 denied, ...
```

The host is not covered by your rules. Check them:

```bash theme={null}
mb-local status --tunnel-name my-tunnel
```

Remember that `*.acme.internal` does **not** match `acme.internal` itself. Use
`.acme.internal` to cover both.

Rules with only IP ranges are another common cause. If you pass
`--only-hosts '10.0.0.0/8'` and your app requests `api.internal` by name, the
name matches no rule, so the request leaves the device normally instead of using
the tunnel. Add the hostname alongside the range:

```bash theme={null}
--only-hosts '10.0.0.0/8,.internal'
```

### The target is not reachable from your machine either

```
[FAIL] target staging.acme.internal   does not resolve: no such host
```

The tunnel is healthy and the problem is between your machine and your service.
A split-tunnel VPN that excludes that subnet is the usual cause. If you cannot
`curl` the service from the machine running `mb-local`, the tunnel cannot either.

### The app ignores the proxy

Some HTTP clients do not use the device's proxy settings. Flutter's `dart:io`
`HttpClient` and Cronet-based stacks are the common ones. Requests from those
clients leave the device directly and never enter the tunnel, so they fail to
reach internal hosts while the rest of the app works.

For Flutter, point the client at the proxy explicitly in test builds:

```dart theme={null}
final client = HttpClient()
  ..findProxy = (uri) => HttpClient.findProxyFromEnvironment(
        uri,
        environment: {"http_proxy": proxy, "https_proxy": proxy},
      );
```

If you cannot change the app, contact support. Android emulators can be booted
with the proxy applied below the app layer, which catches every client.

### The key is rejected

Exit code `10` means the key is not recognised. Exit code `15` means the key is
valid but was issued without the `tunnel:connect` scope, so it cannot open
tunnels. The second one is fixed by an administrator, not by changing the secret.

### `localhost` does not work

On a device, `localhost` is the device itself. Point the app at `mb-local.net`
instead, which resolves to `127.0.0.1` and is mapped back to loopback on the
machine running the tunnel.

If requests then return `400 Bad Request` or a host header error, your framework
is rejecting the `Host: mb-local.net` header. Add `mb-local.net` to its allowed
hosts: `ALLOWED_HOSTS` in Django, `config.hosts` in Rails.

### Some requests work and others do not

If the failing requests are all to the same service, check whether it uses HTTP/3
over QUIC. QUIC does not travel through the tunnel. Most mobile clients fall back
to TCP automatically. If yours does not, disable HTTP/3 in test builds.

## Tunnel lifecycle

### "A tunnel with this name is already connected"

Exit code `13`. Another process holds that name, usually a parallel CI job or a
tunnel left over from an earlier run.

* In CI, give every job a unique name. See
  [Give every job a unique tunnel name](/test-agent/local-testing-ci#give-every-job-a-unique-tunnel-name).
* Locally, stop the other one with
  `mb-local --daemon stop --tunnel-name NAME`, or replace it with `--force`.

MobileBoost refuses the second connection rather than taking over the first, so a
running test is never disconnected by a colleague starting a tunnel.

### The tunnel keeps reconnecting

A growing `reconnects` count in `status` means the connection is unstable.
`mb-local` reconnects automatically and new requests keep working, but requests
already in flight fail when a reconnect happens.

Common causes are laptop sleep, Wi-Fi switching, and a VPN that drops. For
anything long-running, use a [shared tunnel](/test-agent/local-testing-ci#run-a-shared-tunnel)
on a stable host.

### A run fails immediately with a tunnel error

If the named tunnel is not connected when a run starts, the run fails right away
instead of starting and timing out on every request. Check that the tunnel is up
with `mb-local status`, and that the name in your payload matches exactly.

## Still stuck

Send support two things:

```bash theme={null}
mb-local doctor --key $MOBILEBOOST_API_KEY --json > doctor.json
mb-local status --tunnel-name my-tunnel --json > status.json
```

Neither contains credentials or any of your traffic.
