Troubleshooting
Ordered roughly by how often each one comes up. Most have a symptom that points somewhere other than the cause, so the misleading reading is called out too.
Every request fails verification
Symptom: every capability call returns 401 with signature_mismatch.
The name of this failure points at the credential, and the cause is usually the body. Check the body first — rotating a working secret costs you a redeploy and fixes nothing.
1. Something parsed the body
The signature covers the exact bytes received. JSON.parse then JSON.stringify will not reproduce them — key order, whitespace and unicode escaping all differ.
// Wrong — express.json() discards the bytes the signature covers.
app.post("/cheela/execute", express.json(), handler);
// Right
app.post("/cheela/execute", express.raw({ type: "*/*" }), handler);The Express handler detects this and answers 400 raw_body_required rather than letting it read as a bad secret. Any other body-parsing middleware upstream causes the same thing without that message.
2. The secret is empty
// Wrong — verifies every signature against "".
secret: process.env.CHEELA_RUNTIME_SECRET ?? "",A security parameter should not have a fallback. Throw when it is missing.
3. Clock skew
timestamp_outside_tolerance means more than five minutes of drift in either direction. Future-dated timestamps are rejected too, since one would otherwise extend its own replay window.
4. Multiple instances, one memory store
nonce_replayed on legitimate traffic means MemoryNonceStore behind a load balancer. Back it with Redis — the runtime reference has a working implementation.
The model never calls my capability
- Is it deployed?
cheela statusshows the live capability set and diffs it against your local one. Registering locally is not deploying. - Does it have an input schema? Without one, the model is told the capability takes no parameters, so it will not pass the arguments your handler needs. Check the deploy warning.
- Is the description a decision rule?
"Order lookup"gives a model nothing to act on. Say when to use it, and how it differs from its nearest neighbour. - Is your endpoint reachable? A runtime with no working endpoint fails the call rather than announcing the problem.
Read the execution trace. The transcript contains every tool_call and tool_result, so you can see whether the model tried and failed, or never tried.
Deploy warns about missing schemas
⚠ No input schema published for: catalog-search
The model will be told these capabilities take no parameters.Usually one of:
- The schema was assigned to a variable that is never passed as
input. - The schema could not be serialized to JSON Schema. Transforms, effects, and lazy recursive types are the usual suspects — simplify the outermost layer.
- It genuinely takes no input, in which case the warning is correct and you can ignore it.
A capability name is rejected
Invalid capability name "catalog.search". Dots are not allowed: LLM
tool-calling APIs reject them, so the model could never invoke it.
Use hyphens instead, e.g. "catalog-search".Names must match ^[A-Za-z][A-Za-z0-9-]{0,63}$. Dots break tool calling; underscores break the discovery spec; hyphens satisfy both. The dots a published name needs come from your adp.namespace.
The same check runs in the runtime, the CLI, and the control plane, so a name that passes one passes all three.
Authentication failures
| MESSAGE | CAUSE |
|---|---|
| Could not authenticate this Runtime | CHEELA_API_KEY is invalid, or is not a deploy key. |
| This key is not valid for this endpoint | Right key, wrong plane — a ch_pk_ key sent to a deploy route, or a ch_sk_ key sent to execute. |
| Missing runtime API key | No Authorization: Bearer … header arrived. |
| Could not reach the Cheela Control Plane | Network. |
It tells you the key is not valid here, not which of the two you are holding. Check the prefix: ch_sk_ deploys, ch_pk_ executes.
Capability not found
A 404 from the public broker means one of three things, and it will not tell you which — that is deliberate, so anonymous callers cannot probe which runtimes exist.
- The runtime id does not exist.
- The capability is not in the current deployment.
- The runtime has no endpoint configured. This is the one people miss.
Check the third with cheela status or the runtime detail endpoint. Set it in cheela.config.ts, on the dashboard card, or over the API.
The widget is blocked in the browser
A 403 on /v1/runtime/execute from a browser, working fine from curl, means the origin allowlist.
{ "origins": ["https://app.example.com/"] } // trailing slash
{ "origins": ["https://app.example.com/chat"] } // path{ "origins": ["https://app.example.com"] }An entry with a path or trailing slash can never equal a browser’s Origin header. These are now rejected at write time with a message naming the origin to use — but a list saved before that check existed will silently match nothing. Re-save it.
Remember to include every origin you serve from, including www and bare-domain variants if both resolve.
Config and loading errors
Could not load Cheela Runtime
The module at runtime must default-export a Runtime, or export one named runtime. The error names the path it tried.
const runtime = new Runtime();
// ...registrations...
export default runtime; // ← this lineInvalid cheela.config.ts
Every failing field is listed with its own message. The two most common:
- Missing CHEELA_API_KEY —
.envis not being found, or the variable is not set. The CLI walks up from the working directory to find the project root. - endpoint must use https:// — see below.
endpoint must use https
http:// is accepted only for localhost, 127.0.0.1 and [::1]. Everything else needs TLS, because the signature protects integrity rather than confidentiality and the end user’s credential travels in the body. Use a tunnel in development.
An old config with provider and model
Those fields no longer exist. Remove them — executions run on Cheela’s own credential. See Configuration.
The manifest will not publish
| RESPONSE | FIX |
|---|---|
| 404 — no deployment yet | Run cheela deploy first. The manifest is built from the stored deployment, not from local files. |
| 400 — no website or adp.namespace | Add both to cheela.config.ts and redeploy. The manifest cannot describe who operates the system without them. |
| 400 — capability has no version | The discovery spec requires one on every capability, even without schemas. |
All three refuse rather than emitting a partial document. A manifest gets cached and republished by agents nobody can contact, so a wrong one is far more expensive to retract than a missing one is to wait for.
Rate limits
A 429 rate_limit_exceeded means the hourly allowance is spent or the request rate is too high.
- Check your usage. The billing usage endpoint returns
periodStartandperiodEnd, so you can see exactly when the bucket refills. - Remember rollover. Capacity is your hourly rate times your rollover window, so a burst after a quiet period is expected to work.
- Published a manifest? Anonymous broker traffic spends your quota. It draws on a sub-allowance so it cannot starve your own widget, but it is still counted.
Ceilings per plan are in Errors and limits.