Persistent background Monitor (tail -F) dies with exit 144 at nearly every turn boundary

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Aug 7, 2026

Summary

A persistent: true Monitor running a long-lived tail -F repeatedly dies with exit code 144, at roughly every turn boundary. Re-arming works, and then it dies again — sometimes within seconds. This makes an event-driven architecture built on persistent Monitors unreliable: the agent silently stops receiving events and is indistinguishable from idle.

Environment

  • Claude Code 2.1.220
  • macOS 26.2, arm64
  • Node v22.22.0

Reproduction

Arm a persistent Monitor on a file that is appended to occasionally:

Monitor({
  command: "mkdir -p <dir> && touch <notify> && exec tail -F -n0 <notify>",
  description: "inbox",
  persistent: true,
})

Then work normally. Over a single ~90-minute session, this Monitor died with exit 144 approximately 30 times — essentially every turn boundary, sometimes within seconds of being re-armed.

The failures were uncorrelated with:

  • the content of commands run in the session
  • elapsed time since arming
  • /compact (never invoked in that session)

This was observed independently across four separate Claude Code sessions on the same machine, each with its own Monitor on its own file.

What we ruled out

The watched files are written with a plain fs.appendFileSync followed by chmodSyncno rename, no truncate, no unlink, so the classic "tail loses its inode" explanation does not apply. tail -F would survive that anyway. We inspected the writer specifically to rule this out before filing.

Running the same tail -F command directly in a shell, outside the Monitor tool, does not exhibit this — it stays alive indefinitely.

Note on the exit code

144 = 128 + 16. On macOS signal 16 is SIGURG, which is normally harmless and routinely used for runtime preemption — a process being killed by it suggests something is not handling/ignoring it rather than a deliberate termination. Offered as an observation, not a diagnosis.

Impact

We build an event-driven multi-agent system on top of persistent Monitors: each agent tails a notify file and wakes when work arrives. When the Monitor dies:

  • the agent stops receiving events entirely — it is deaf, not idle, and the two look identical from outside
  • self-healing is structurally blocked: the agent re-arms its Monitor at the start of each turn, but the Monitor is what causes turns. A Monitor that dies while the agent is idle can never reach the code that would re-arm it
  • the only escape is a long fallback timer, so an agent can be unreachable for 20–30 minutes

We can mitigate the consequences (warn on dispatch, surface it in a health check) but not the cause — nothing outside a session can inject a wake into a dormant one.

One additional data point from an earlier occurrence: a dead Monitor's .output file had grown past 5GB before anyone noticed.

What would help

Even without a fix, surfacing why the process was terminated (which signal, and whether the harness or the OS sent it) would let us distinguish a harness lifecycle event from a genuine crash. Right now exit 144 is all we get.

View original on GitHub ↗