# Troubleshooting

> Quick fixes for the problems people actually hit across OpenEng: an engineering app that can't reach its engine, one-time keys and the limit of five unused keys an hour, reconnecting, connectors that answer 401 or 403, and the OpenEng AI engine, Console and updates.

Quick fixes for the things people actually hit. Each guide also ends with
its own troubleshooting block — this page collects the cross-cutting ones.

The first section covers the five engineering apps; the second covers OpenEng AI. Problems with a
single topic have their own lists: installing ([Install](install.md#troubleshooting)), the AI engine's
startup messages ([The engine](engine.md#troubleshooting)), the Console and the Web Console
([Quickstart](ai-getting-started.md#troubleshooting)), and plans and payments
([Plans & billing](account.md)).

## Engineering apps

These apply to Terminal, Kubernetes, Data, Git and Chat alike. The examples use Terminal; for another
app, swap in its engine (`openeng-kubernetes`, `openeng-data`, …) and its port. The Git and Chat engines aren't published yet, so their pairing screens can't be completed today ([Install](install.md#installers)).

### The app says "No engine reachable on this machine"

The app keeps you on its pairing screen until it can reach the engine on your machine. Work down this
list:

1. **Start the engine** with the command the pairing screen shows, in a terminal on the same machine as
   the browser. When it's up, it prints a line like
   `openeng-terminal: serving gRPC + gRPC-Web (sealed) on … — Ctrl-C to stop`. The app notices within
   a few seconds on its own; **Retry connection** checks at once.
2. **Read what the engine printed.** If it stopped with a key error, see
   [One-time keys](#one-time-keys). If it says
   `port 127.0.0.1:50052 is held by another process that is not an OpenEng terminal engine`, another
   program is using the engine's port (Terminal 50052, Git 50053, Kubernetes 50054, Chat 50056, Data
   50057): stop that program and start the engine again. An older copy of the same engine is replaced
   automatically.
3. **Check the certificate line.** If the engine printed `cloud cert fetch failed (…) — using
   local/self-signed cert`, it couldn't download the certificate for `engine.openeng.app`, and browsers
   won't accept the fallback. Make sure the machine can reach `api.openeng.app`, then start the engine
   again with a fresh command. (The engine also prints the commands to trust its own local certificate
   authority, if you'd rather do that.)
4. **Check that `engine.openeng.app` resolves to `127.0.0.1`.** The app reaches the engine at
   `https://engine.openeng.app:<port>`, a public name that points at your own machine. Run
   `nslookup engine.openeng.app` (or `getent hosts engine.openeng.app` on Linux); the answer must be
   `127.0.0.1`. Some routers and DNS filters block public names that point at a local address; allow
   `engine.openeng.app` in them.
5. **Allow local access in the browser.** If the browser asks whether the app may connect to apps or
   devices on your local network, allow it: the engine is a program on your machine.
6. **Restart the engine.** Each start checks for an update first, and an engine too old for the app
   can't open a session with it.

### One-time keys

**The engine exits with `cannot start: the engine requires --oauth=<one-time key> (copy it from the Connect screen)`.**
You ran it without a key; the installer's closing hint leaves the key out. Copy the whole command from
the pairing screen.

**The engine prints `oauth exchange failed: oauth exchange refused: …`**, for example
`token already consumed`. A key works once and for about five minutes. On the pairing screen, press
**Regenerate** and run the new command. The line under the command shows the key's state:
`one-time key · expired` means it's time to regenerate.

**"Key limit reached".** The OpenEng API mints at most five unused keys an hour per account, shared
by every OpenEng app (a key an engine has used no longer counts), and each app also counts the keys
it mints in your browser. The button shows `Limit reached — try again in …`. The wait it shows is
the worst case: after a minute, **The wait shown is worst-case — try anyway** lets you try again.
Reloading the pairing screen doesn't use up a key, because the app reuses a key that hasn't expired.

### The app lost its engine

If the engine stops answering, the status dot in the app's header shows it, and the app keeps
checking. If the engine comes back, the app reconnects on its own. If the engine exited, start it
again: every start needs a fresh command from the pairing screen, and once it's up the app returns you
to the page you were on. When the app offers **Restart engine** for a downloaded update, the engine
restarts into the new version and the app reconnects without a new key. The one exception: the first
such restart after updating from Terminal or Data 0.0.3, or Kubernetes 0.0.4, asks for a fresh one-time
key once (updating at the next start needs nothing). See the app's own page for
what it keeps across a restart, for example [Terminal](terminal.md#engine-offline).

### A cloud connector answers 401 or 403

Kubernetes and Data ask for new credentials when a connector rejects the stored ones: Kubernetes shows
**Not authorized to access this cluster** and Data shows **Re-authenticate connector**. Enter the
corrected API key (and the bearer token or write key, if you use them) and press **Save & retry**; the
dialog stays open with an error while the connector still refuses. **Cancel** leaves the connection
marked inaccessible, with a bar across the workspace to re-enter the credentials later.

If the key looks right, check the gateway in front of the connector: the API key the app sends as
`x-api-key` must be one that gateway accepts ([Connectors](connectors.md#adding-a-connector)).

A read-only connection isn't an authentication problem. Without a valid write key a connector is
read-only, and the apps block the actions that would change something. Kubernetes shows
"Read-only cluster — actions are disabled", and Data shows "Read-only connection — write / update /
delete statements are blocked by the connector". Add the connector's write key to allow changes
([Read-only by default](connectors.md#read-only-by-default-per-request-write-key)).

### Signing in

**Sign-in doesn't complete.** Every app signs in with Google through `auth.openeng.app`. If the browser
blocks the sign-in popup, the apps fall back to signing in on the full page. Allow popups and site
data for the app and for `auth.openeng.app`, then try again.

## OpenEng AI

The AI engine (`openeng`), the Console and the Web Console.

### Install & startup

**"command not found: openeng" after install** — the install dir isn't on
your `PATH` in this shell. Open a new terminal, or add the path the
installer printed (override the location with `OPENENG_BIN_DIR` when
installing).

**The installer is downloading gigabytes** — that's the default chat model, which the AI installer
fetches with `openeng pull chat` (a few GB, one time). Skip it at install time with
`OPENENG_NO_MODELS=1`; the engine then downloads the models a config needs when it starts with that
config. Fetch a model by hand anytime with `openeng pull <role>`
([Embedded models](engine.md#pull)).

**`local model inference refused (no functional sandbox enclosure)`** — the engine runs its built-in
local models only inside a sandbox, and none works on this machine. On Linux (and in WSL) it needs a working
systemd user session or bubblewrap with unprivileged user namespaces; containers and CI runners often
have neither. Install bubblewrap, enable a systemd user session, or run the engine outside the nested
container, then restart it. Hosted, external-CLI and Ollama (catalog) models keep working meanwhile
([Local models need a sandbox](engine.md#local-model-sandbox)).

**Slow, or out of memory on a small machine** — model weights are the big cost. Bind smaller models to
the config's role groups (the Models step disables models the machine profile can't run), and see
[Runs feel slow](#agents-permissions) below for the engine's share of the machine.

### Engine keys & sign-in

**The engine won't start and asks for a key** (`cannot start: no OpenEng key …`) — the AI engine has
no browser sign-in. It starts only with an OpenEng key, an API key you issue in the Console under
Deploy → API keys: `openeng serve --key=<key>`, or set `OPENENG_KEY`. Every other startup message, such
as a rejected key or a missing Pro plan, is explained in [the engine's troubleshooting](engine.md#troubleshooting).

**`this key already runs an engine — stop it or generate a new key`** — one key runs one engine at a
time. Stop the engine that's using it, or issue another key if your plan allows one
([Plans & billing](account.md#plans)).

**Headless box / CI** — use the same kind of key: issue an API key in
the Console and set `OPENENG_KEY`
([API keys & sandboxes](api-keys-sandboxes.md)). In the current engine build, though,
`openeng headless` checks its key, prints its driver and exits, so unattended and CI runs don't work
yet ([Headless machines](engine.md#headless)).

### The Console

**"The platform API isn't reachable right now" banner** — the Console retries automatically;
*Retry now* forces it. Editors mirror drafts locally, so nothing is lost.

**"Plan unverified — Refresh"** — the plan check failed transiently and the
Console is showing the Free view defensively. One click re-checks.

**Publish is disabled** — the **Review & publish** step lists every blocking problem. The usual: an
empty agent/scheduler the engine would silently drop, a plain-`http://` credentialed URL, a raw secret
value, or more enabled items than your plan allows. The version must also be valid SemVer and higher
than the last published one ([Configs](configs.md)).

**A publish doesn't reach a machine** — the engine resolves its default config when it starts, so
restart it to pick up a new publish. In the Web Console, each session runs the version you picked:
pick the new version. Also check the machine isn't serving a different config (`--default` or
`OPENENG_DEFAULT_CONFIG`; [Which config the engine serves](engine.md#config-selection)).

**"Restore unsaved draft?" on opening an editor** — a previous session left
locally mirrored edits that differ from the server copy. *Restore draft* keeps
them, *Discard* clears the mirror.

### Agents & permissions

**"The agent won't edit my files"** — the agent works inside the directory you started the engine in,
so start the engine from your project's directory ([Start the engine](engine.md#start)). The first time
the agent wants to change something in a directory the engine doesn't trust yet, the Web Console shows
a **Permission needed** prompt: **Approve** trusts that directory, and the engine remembers it in
`~/.openeng/trust.json`. If you denied it, the agent can't change anything there for the rest of that
run. Shell commands are confined too: when bubblewrap works on the machine, by default they can write only inside the working directory, so also check `OPENENG_SANDBOX`
isn't set to `read-only` in that shell.

**"The agent won't run shell commands"** — the agent runs shell commands only in a directory the engine
trusts (see above), or in any working directory when the engine was started with
`OPENENG_AGENT_SHELL=1`.

**The agent did something you didn't want** — the Web Console asks before gated actions with a
**Permission needed** prompt, and **Deny** refuses. Keep the default
sandbox so shell commands can write only inside the working directory, and set a
[token limit](govern.md) for unattended work. That default applies only when bubblewrap works on the
machine; otherwise shell commands run unconfined (see `OPENENG_SANDBOX`
in [Environment variables](engine.md#environment)). When confinement matters, install bubblewrap and
make sure unprivileged user namespaces are allowed (containers often block them).

**Runs feel slow / machine stays calm** — that's the engine's resource cap: its share of CPU and memory
comes from the config's Resources step (10% when no config is published). Raise it in the config, or on
one machine with `OPENENG_RESOURCE_FRACTION=0.25` ([Environment variables](engine.md#environment)).

### Cells & connectors

**A cell, MCP server, model or plugin is missing from a run** — its `${ENV_VAR}` placeholder probably
has no value on this engine: the engine disables anything whose secret is unset rather than send an
empty one. The Web Console's **Placeholder values** screen lists every name the config needs and
whether it's set. Set the variable in the shell that starts the engine (`export PROD_CELL_KEY=…`) and
restart the engine, or give it a value on that screen.

**The cell answers HTTP 401/403** — the gateway rejected the credential. Check that the variable
holds the key your gateway issued and that the gateway has a usage plan or key attached. Also check
which header the gateway reads: the engine sends a cell's credential as `Authorization: Bearer …`,
while the cell packages' deploy guides set the gateway up to read `x-api-key`
([Cells › Troubleshooting](cells.md#troubleshooting)).

**A credentialed cell/MCP server is silently absent** — plain `http://` to a
remote host is refused by design. Use `https://`.

**A write cell doesn't appear in the toolbox** — write cells are only available in an execution (goal)
context, never in read-only analysis. That's the safety model, not a bug ([Cells](cells.md)).

**An integration is refused** — an integration (Console → Connect → Connectors) must be a remote
`https://` endpoint at a declared host-isolated sandbox tier; the Console won't publish one that isn't,
and the engine won't run it ([Integrations](integrations.md)).

### Fleet

**A sandbox node never appears** — in the current engine build, `openeng headless` checks its key,
prints the driver it resolved and exits; it doesn't register a node or run fleet work
([Headless machines](engine.md#headless)). The Sandboxes page's empty state suggests
`OPENENG_KEY=… openeng sandbox`, but the engine has no `sandbox` command.

**A node is online but idle** (once sandbox mode ships) — on Pro only the *active* node receives
runs, and your key must be bound to it ([API keys & sandboxes](api-keys-sandboxes.md)).

**Lost an API key value** — unrecoverable (only the hash is stored). Revoke
and reissue.

### Plan-gated features

**The engine says `an active Pro plan is required to run an engine`, or a Console page shows a lock** —
running the AI engine needs Pro, and so do schedulers, API keys, sandboxes, storage points, Human-in-the-Loop,
behavioral roles and Device Use. Pro launches in Q4 2026 and can't be purchased yet, so there's no
trial to start; the Free plan includes authoring and publishing in the Console once the OpenEng AI
services are live
([What each plan includes](account.md#plans)).

**The Console can't load or save anything, or the engine can't register** — as of September 25, 2026
the OpenEng AI services behind them (the OpenEng AI side of the OpenEng API, and the Broker) aren't
live yet, on any plan ([OpenEng AI](ai.md)). The engineering apps aren't affected.

## Updates

**Stuck on an old version** — run the installer again, or download fresh
from [openeng.app/download](https://openeng.app/download). For an app engine,
`openeng-terminal --version` (or the account menu in the app) shows what you're on;
[releases.openeng.app](https://releases.openeng.app) shows what's current. The AI engine has no version
command; running its installer again always fetches the current build ([Updates](install.md#updates)).

**An update was refused with a checksum error** — that's the integrity
check protecting you from a corrupted download. It aborts rather than
installing; retry, and if it persists run the installer again (the commands are on
[openeng.app/download](https://openeng.app/download)) or download the binary and check it
yourself ([Check a download yourself](install.md#verify-a-download);
[Release integrity](security.md#release-integrity)).

**An app shows "Engine update … ready — restart the engine to apply it"** — the engine downloaded and
checked a newer version while it ran. Press **Restart engine** when it suits you; otherwise the update
applies the next time you start the engine.

## Still stuck?

Include the engine and its version (`openeng-terminal --version` for an app engine), your OS, and the
exact command + output. The FAQ at [openeng.app/faq](https://openeng.app/faq) covers more,
[feedback.openeng.app](https://feedback.openeng.app) takes bug reports, and
[hello@openeng.app](mailto:hello@openeng.app) reaches us directly. Report security issues privately
([Reporting a vulnerability](security.md#reporting-a-vulnerability)).
