Troubleshooting
Match the symptom to the fix below. If the symptom does not fit any section, escalate to Tiny Support with the output of docker logs ai-service --tail 200 and a redacted copy of the PROVIDERS value.
Quick triage
Work through this list to identify the symptom area:
-
Is the container running? Run
docker psto check. If the container has exited or will not pull, see Container startup failures. -
Does
/healthreturn 200? Runcurl -fsS http://localhost:8000/health. If it times out or returns 5xx, see Container startup failures. -
Does the API return an auth error? If the response is 401,
allowed:false, orinvalid-jwt-*, see API and JSON Web Token (JWT) authentication. -
Does the SSE stream carry an
event: errorfrom the LLM? If yes, see Large language model (LLM) provider errors. -
Is the editor side broken? Missing toolbar, token 401, or hanging stream? See Editor and front end.
-
Slow, timing out, or failing under load? See Performance.
-
Scaling, upgrades, or deployment questions? See Production deployment.
-
Need the meaning of a service log message? See Log reference.
If none of the above match, see Diagnostic recipes and then escalate to Tiny Support.
Container startup failures
Run docker logs ai-service first. All entries below assume the log output is available.
| Error / symptom | Cause | Fix |
|---|---|---|
|
Key was truncated, contains a line break, or has surrounding whitespace |
Paste the key as a single unbroken line. Verify the first and last eight characters against the original. |
|
|
Switch to |
|
The MySQL user lacks the listed privileges on the AI service database. Privileges granted globally ( |
Grant the privileges on the AI service database itself ( |
|
Postgres schema not pre-created |
Run |
|
The managed PostgreSQL server requires TLS ( |
See Managed database TLS. |
|
Several replicas started at the same time against a new database and ran the schema migrations concurrently. |
The replica restarts and recovers on its own. To avoid the error, start one replica, wait until it is healthy, then scale up. See Upgrade process. |
|
|
Pin |
|
A |
Correct the |
|
A |
Start from the provider example on the LLM providers page and add fields one at a time. |
Container exits with no useful log |
Missing required env var, or malformed JSON in |
Run |
Pods stay unready on Amazon EKS Auto Mode, restart after failed liveness probes, and |
The database and Redis security groups allow inbound traffic from the node security group, but EKS Auto Mode attaches the cluster primary security group to the pods. |
Allow inbound database and Redis traffic from the cluster primary security group. See Infrastructure recommendations. |
|
Port mapping missing |
Add |
|
AI service is on a different Docker network from the data layer |
Use |
API and JSON Web Token (JWT) authentication
These assume the container is running and /health returns OK.
| Error / symptom | Cause | Fix |
|---|---|---|
|
Token signed with the wrong key. Most commonly, signed with |
Re-copy the API Secret from the Management Panel at |
|
|
Copy the Environment ID from |
|
Token is past its |
Issue tokens with a reasonable lifetime (for example |
|
Environment was not created through the Management Panel UI |
Delete and recreate the environment through |
JWT silently rejected |
Token signed with RS256 instead of HS256 |
Re-sign with |
|
Stream abort left temporary state blocking the conversation |
Start a new conversation or reload the page. Custom UIs should create a fresh conversation after cancel. |
|
|
Use the explicit array form:
Common mistakes: |
Large language model (LLM) provider errors
Most of these appear as event: error inside the SSE stream, and the HTTP response is still 200. NoValidApiKeysFoundError is the exception: the message request fails with HTTP 500, and the cause appears in the container log.
Cloud providers (OpenAI, Anthropic, Google)
| Error | Fix |
|---|---|
|
Update the key in |
AWS Bedrock
| Error | Fix |
|---|---|
|
Inline |
|
Enable model access in Bedrock console → Model access. Attach an IAM policy with |
|
Complete the AWS Marketplace subscription for Anthropic in Bedrock console → Model access → Anthropic. |
|
Use the region-prefixed inference profile ID (for example |
Google Vertex AI
| Error | Fix |
|---|---|
|
Inline |
Auth errors with a valid service account |
|
|
Run |
Blocked by GCP org policy |
Check |
Azure OpenAI
| Error | Fix |
|---|---|
Model not found / |
|
API errors with no provider message |
Set |
OpenAI-compatible (Ollama, vLLM, LM Studio)
| Error | Fix |
|---|---|
"Not Found" in SSE error |
|
|
Start Ollama with |
"does not support tools" |
Use an official model ( |
|
Model is too slow for the default timeout. Set |
Editor and front end
Confirm /health is OK and a direct curl to /v1/conversations works before investigating the editor.
| Symptom | Fix |
|---|---|
No AI buttons in the toolbar |
Ensure TinyMCE 8+ is loaded, |
Token fetch returns 401 |
The token endpoint’s own authentication middleware is rejecting the request. Check session cookies, Cross-Origin Resource Sharing (CORS) credentials, and bearer tokens in the browser network tab. |
Token returned but rejected by the AI service |
See API and JSON Web Token (JWT) authentication above: wrong secret, wrong |
AI responses hang in the browser |
The reverse proxy is buffering the SSE stream. Set |
CORS error on |
Add the editor’s origin (scheme + host + port) to the |
Editor renders then disappears (Next.js / Nuxt / SvelteKit) |
TinyMCE references |
|
Token endpoint is returning an invalid JWT or non-JSON response. Test with |
Performance
| Symptom | Fix |
|---|---|
Self-hosted model is slow through the AI service compared with raw |
Co-locate the inference server with the AI service. Use a smaller or more quantized model. Disable telemetry during development ( |
Containers OOM or MySQL takes 60+ seconds to start (Colima) |
Default Colima VM is too small. Run |
Log reference
Read the AI service log with docker logs ai-service, or with kubectl logs on Kubernetes. To collect the log from several instances in one place, see Distributed logging.
Log format
Error and fatal entries are JSON objects, one per line:
| Field | Content |
|---|---|
|
The severity, as a number: |
|
The time of the entry, in ISO 8601 format, in UTC. |
|
The message. Error and fatal messages start with |
|
Optional. Details of the entry, such as the error |
|
Optional. The stack trace of an error. |
For example, a PostgreSQL deployment without the required schema stops with this entry:
{"level":60,"time":"2026-05-05T11:19:07.634Z","msg":"[FATAL] PostgreSQL Error: schema \"cs-on-premises\" does not exist","data":{"name":"PostgreSQL Error","message":"PostgreSQL Error: schema \"cs-on-premises\" does not exist"}}
To show only error and fatal entries, filter the log with jq. The fromjson? filter skips any line that is not JSON:
docker logs ai-service 2>&1 | jq -R -c 'fromjson? | select(.level >= 50)'
Notable log messages
Start-up failures are logged with the prefix [FATAL] and stop the AI service. For those messages and their fixes, see Container startup failures. For MCP and web search messages, see MCP troubleshooting.
| Message | Meaning | Action |
|---|---|---|
|
The AI service started and accepts requests. |
No action. |
|
The message request failed with HTTP |
Add inline credentials for the provider. See Large language model (LLM) provider errors. |
|
The file upload failed with HTTP |
Turn off uniform bucket-level access on the bucket. See GCP (GKE). |
Tracing a failed request
Every error response from the AI service carries a traceId that identifies the request. For the error response shape, see Error responses.
The TinyMCE AI plugin prints each error in the browser console as a TinyMCE AI Error: entry, followed by the status code, a Trace ID: line, and the error code.
Diagnostic recipes
Expand for copy-ready diagnostic commands
Tail logs:
docker logs ai-service --tail 200 -f
Liveness check:
curl -fsS http://localhost:8000/health
Decode a JWT (inspect payload without verifying):
python3 -c "import jwt,sys,json; print(json.dumps(jwt.decode(sys.argv[1], options={'verify_signature': False}), indent=2))" <token>
Recreate after an env change:
docker compose up -d --force-recreate ai-service
Inspect effective environment:
docker inspect ai-service | jq '.[0].Config.Env'
Validate PROVIDERS JSON:
echo "$PROVIDERS" | jq .
Test data layer connectivity from inside the container:
docker exec ai-service /bin/sh -c "nc -zv mysql 3306"
docker exec ai-service /bin/sh -c "nc -zv redis 6379"
Replace mysql and redis with the actual hostnames configured in DATABASE_HOST and REDIS_HOST. If the AI service was started with docker compose rather than standalone docker run, use docker compose exec ai-service instead.
End-to-end smoke test (token mint through streamed response):
TOKEN=$(curl -s -X POST http://localhost:3000/api/ai-token | jq -r '.token')
curl -s -X POST http://localhost:8000/v1/conversations \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":"smoke-1","title":"Smoke test"}'
# Replace 'agent-1' with a model ID from MODELS if explicitly configured
curl -N -X POST http://localhost:8000/v1/conversations/smoke-1/messages \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"prompt":"Say hi in five words.","model":"agent-1"}'