Orphaned Image Callbacks Are Poisoning Isolated Cron Runs

Orphaned Image Callbacks Are Poisoning Isolated Cron Runs

The uncomfortable thing about OpenClaw issue #86490 is that the bug does not merely fail a cron job. It changes who gets to speak first in the next run.

After upgrading to OpenClaw 2026.5.22, an isolated cron job that calls image_generate can leave behind a failed run’s callback. On the next execution of the same job, that stale callback arrives as sequence 1, before the configured cron prompt. The agent processes the image without the intended context, may send it to unrelated channels, and then fails again with stopReason=toolUse. The next orphan repeats the cycle. The reporter saw it fail 5 out of 5 times, with run durations around 130 to 138 seconds.

That is a crisp reliability bug. It is also a clean reminder that “isolated session” has to mean more than “we used a familiar session-key prefix.” In an agent runtime, the first user message establishes intent. If a tool callback from a dead run can become that message, isolation has already lost.

A dead run’s artifact is not a fresh instruction

The reproduction path is straightforward. Create a cron job with sessionTarget: isolated and payload.kind: agentTurn. Have it call image_generate. Terminate before the image callback returns because of a tool error, billing interruption, or similar failure. Trigger the same job again. Instead of receiving the cron prompt as the first input, the new run receives the stale image callback as role=user, with provenance showing sourceTool=image_generate.

The evidence is better than the usual “cron seems haunted” report. The issue includes session history where seq 1 is a tool callback, no prior cron prompt, and anomalously low input token counts: 16 to 21 tokens versus healthy runs at 33 to 52. The reported version range is also useful: last known good 2026.5.18, first known bad 2026.5.22. The suspected link is the 2026.5.22 changelog item that moved image generation to “separate session-backed background tasks with message-tool completion delivery.”

That architectural direction is reasonable. Long-running media generation should not pin the original model turn forever. But callbacks need ownership. A background task result should target the exact live run that created it, or land in an artifact store with explicit recovery semantics. It should not be allowed to wander into the next isolated run because the job name looks close enough.

Callbacks are control flow, not just content

The bug matters because agent systems do not treat all messages equally. A stale image callback delivered as role=user is not simply a misplaced attachment. It is a control-flow event. It can steer the model before the real cron instruction arrives. In this report, the payload is an image generation result. In the general case, background callbacks may contain URLs, metadata, tool outputs, failed partial artifacts, provider messages, or user-supplied media. If those can preempt a scheduled prompt, the boundary between “tool output” and “instruction” gets dangerously soft.

This is where reliability and security start to overlap. A terminated run should not be able to inject the opening message of a future run. Even if the callback is benign, the semantics are wrong. The runtime is effectively saying: “This piece of data was once owed to a different execution, but since that execution is gone, we will reinterpret it as the next user’s request.” That is not delivery recovery. That is temporal prompt confusion.

OpenClaw already has the right conceptual ingredients in other areas: session keys, provenance, background task records, message-tool delivery, isolated targets. The missing contract is run identity. Every callback should carry enough ownership data to answer: which run asked for this, is that run still alive, has this callback already been delivered, what is the expiry policy, and what should happen if the original recipient is gone? Without those answers, retries and background tasks become ambient messages looking for a conversation to attach to.

What practitioners should check now

If you operate OpenClaw cron jobs that call media tools, inspect the transcripts. The cheap test is simple: sequence 1 of each scheduled run should be the configured cron prompt or a runtime wrapper around it, not a tool callback. Alert if provenance.sourceTool appears as the first user message. Also look for suspiciously low input-token counts on jobs that normally include a full instruction payload. The issue’s token-count delta is a nice practical signal because it catches the absence of the prompt even if the message text is noisy.

Until a fix lands, avoid reusing session-key prefixes for repeated isolated media jobs, or force unique run-scoped keys when possible. The reporter’s workaround — run the agent as a subagent with a different sessionKey — is not elegant, but it points at the underlying problem: separate identity avoids the poisoned callback path. Operators should also treat failed image-generation runs as potentially contaminating later executions and clear or quarantine pending callbacks before retriggering important scheduled jobs.

For framework builders, the prescription is stricter. Background task completion should be addressed to a run id, not merely a session-like namespace. If the run is gone, the callback should move to one of three explicit states: expired, quarantined, or stored as an artifact for manual retrieval. None of those states should be represented as a new role=user message in a later isolated run. The model should never have to infer that the first thing it sees is actually leftover tool exhaust from a previous failure.

The product surface matters too. Operators need a way to see orphaned callbacks. A UI or CLI view showing “background task completed after owner terminated” would turn this from a mysterious cron poisoning loop into a recoverable operational condition. Silent queue reuse is the enemy here. Visibility is part of the fix.

The larger point is that scheduled agents are only useful if time boundaries are real. A cron run is supposed to mean: at this time, with this prompt, in this state, do this task. If yesterday’s orphaned image can become today’s first user message, the scheduler is no longer the source of truth. The callback queue is.

OpenClaw should fix this with exact run ownership, expiry, and quarantine. Anything less leaves isolated cron jobs isolated mostly by optimism.

Sources: OpenClaw issue #86490, OpenClaw v2026.5.24-beta.2 release, OpenClaw PR #86491, OpenClaw v2026.5.22 release