Build in Public

20 Production Gotchas from Running AI Agents

What 3 months, 89 documented failures, and a 3-agent fleet taught us about AI ops nobody warns you about.

3
AI Agents
89
Documented Gotchas
90
Days in Production
0
Extended Outages

We run a 3-server AI agent fleet β€” one team lead, two executors β€” handling delegation, research, code execution, and monitoring around the clock. Real tasks, real users, real consequences.

Every failure got a number. Every number got a fix. Here are the 20 gotchas that actually matter, ordered by how badly they'll hurt if you don't know about them.

πŸ”₯

I. The Agent Turned On Itself

The most dramatic failures β€” when the AI actively works against its own operation.

01

The Agent Overwrote Its Own Instructions

We asked an agent to "clean up the workspace." It helpfully organized all files β€” including AGENTS.md, the file containing its operational instructions. Replaced the entire thing with a neat summary. The agent no longer knew how to do its job.

The Fix

Read-only bind mounts (:ro) for all critical files. Instructions, identity, config β€” immutable from the agent's perspective.

02

Same Task Ran 11 Times in an Infinite Loop

Agent finishes a research task. Tries to report results using a hostname. Docker can't resolve it. Callback fails silently. Mailbox retries every 16 minutes. For 3 hours. 11 identical runs.

The Fix

All callback URLs use raw IPs, never DNS names. Mailbox now has idempotency guards β€” same task ID can't re-execute if results exist.

03

Agent Reports Went Nowhere β€” For Days

Agents "completed" tasks and wrote beautiful summaries... into their own chat session. The orchestrator never received anything. They were typing their results instead of sending them. Chat messages are local.

The Fix

Made script execution MANDATORY in instructions. Bold. Caps. Repeated three times. Agents still occasionally forget.

04

Stale Memory = Yesterday's Crisis Reported as Today's

Agent's memory says "Agent2 is down" from yesterday. Agent2 has been fine for 20 hours. But the agent confidently reports a current outage. Memory persists; reality changes.

The Fix

Rule: always verify via live check before reporting any issue. We call this "Gotcha #136" and reference it in every operational document.

πŸ‘»

II. Silent Failures That Look Like Success

Everything reports green. Nothing is working. The scariest category.

05

Ghost Tasks Blocked the Queue for Hours

Tasks complete. Results delivered. But the local inbox still says status: working. Queue full. No new tasks accepted. Agent looks broken but finished everything hours ago.

The Fix

Report-back must update both local state AND remote callback. Completed tasks move to archive after successful delivery.

06

One Config Key Crashed the Entire Service

Added capAdd: ["NET_ADMIN"]. Reasonable setting, correct syntax. Service refused to start. Hard crash on any unrecognized key. Agent down for 2 hours.

The Fix

Verified config schema doc. Check before every edit. Rule: "if unsure whether a key exists, don't add it."

07

Cron Jobs Ran "Successfully" for 2 Weeks Doing Nothing

Cron runs as openclaw. Log owned by root. Write fails silently. No error in cron mail because stderr goes to the same unwritable log.

The Fix

Audit every file your cron touches for ownership. If the user can't write it, the cron doesn't schedule.

08

Health Check Lied: "3 Days Stale" Was Actually Fine

Health endpoint: memory_sync_age_seconds: 284137. Three days stale. SSH in β€” sync ran 4 minutes ago. The endpoint just calculated the timestamp wrong.

The Fix

Never trust a single metric. Health endpoints are hints, not truth. Always verify via direct check.

09

Concurrent JSON Reads Returned Half a File

Two processes read the same task file. One gets {"task": "research comp β€” truncated. JSON.parse fails. Task vanishes. No crash, no log.

The Fix

Atomic writes: temp file + rename(). OS 101 that agent frameworks skip.

10

SIGUSR1 + Disabled Service = Silent Death

Restart=on-failure. SIGUSR1 for reload. Service was manually stopped. Process dies, systemd doesn't restart. No alert. Agent gone.

The Fix

Check service state before signals. Or Restart=always with the trade-off.

🌐

III. Networking Lies

Containers, mesh VPNs, and DNS create a reality distortion field.

11

Localhost Returned ECONNREFUSED β€” Service Was Healthy

Service binds to Tailscale IP. Health checks using localhost β†’ ECONNREFUSED. We thought it was down for 3 hours. Running perfectly, just not on 127.0.0.1.

The Fix

Never hardcode localhost. Resolve the actual interface IP. Burned into every health check script.

12

Docker Bridge Can't See the Mesh VPN

Agent in Docker calls a peer via Tailscale hostname. DNS fails. Bridge network doesn't know Tailscale exists. All cross-agent communication dead.

The Fix

network: "host" in Docker config. Plus hardcoded Tailscale IPs. Belt and suspenders.

13

SSH From Docker β€” Tool Not Found

Delegation script SSHs to a peer. Works on host. Docker: no openssh-client. Error swallowed. Task silently dropped.

The Fix

Install tools in Docker setupCommand. Never assume container has what the host has.

14

Hardcoded Callback Broke Peer-to-Peer

All delegate scripts had the orchestrator's IP as callback. Agent2β†’Agent1? Results go to orchestrator. Agent2 waits forever. Only hub-spoke worked.

The Fix

Auto-detect own IP, set callback to self. Results return to whoever sent the task.

πŸͺ€

IV. Infrastructure Traps

Gotchas that hit before your agents even start working.

15

"Sandbox Off" Still Runs Docker

sandbox.mode: "off". Expected bare-metal. Still Docker. Every command containerized. Hours debugging "missing" binaries on the host.

The Fix

Sandbox controls permissions, not Docker. Add explicit bind mounts for everything needed.

16

4GB Servers OOM on Package Install

pnpm install eats all RAM. Process killed. Cloud VPS ships with zero swap.

The Fix

First command ever: fallocate -l 4G /swapfile && mkswap && swapon.

17

chmod 600 on Tokens Broke Docker Auth

Security instinct: chmod 600. Docker runs as different UID. Can't read tokens. Auth fails silently.

The Fix

644 for Docker-readable tokens. Security theater that breaks functionality isn't security.

18

140KB CLI Argument Hit Linux's Invisible Limit

Sync script passed 140KB JSON as CLI arg. ARG_MAX: ~128KB. Process dies with zero error. Fleet sync silently broke.

The Fix

Pipe through stdin or temp files. Never >10KB as CLI args.

πŸ€–

V. The AI-Specific Weirdness

Gotchas that only exist because your workers are language models, not scripts.

19

Typing Indicator Survived Agent Death

Agent starts task. Telegram: "typing..." Session crashes. Indicator stays forever. Users think it's working. Dead for hours. Nobody notices because it looks busy.

The Fix

Explicit cleanup on session end. Watchdog clears stale indicators after timeout.

20

Search Broke on Hyphens

gotcha-143 β†’ nothing. gotcha 143 β†’ found. SQLite FTS5 treats hyphens as token separators. Our entire knowledge base uses hyphens.

The Fix

Spaces not hyphens in queries. A punctuation mark cost hours of debugging "missing" knowledge.

The Meta-Gotcha

The biggest lesson isn't any single failure β€” it's that AI agents find failure modes humans never would. They'll overwrite their own instructions, report yesterday's problems as today's crisis, and run the same task 11 times in a loop.

Every gotcha above was discovered in production. Not testing, not staging β€” production. The only defense is documenting everything, verifying before reporting, and assuming that whatever can break will break in a way you didn't expect.

90 days. 89 gotchas. Zero extended outages. Not because nothing breaks β€” but because every break becomes a numbered lesson the fleet never repeats.