openclaw - 💡(How to fix) Fix [Feature]: Per-job acceptSilentStop flag — treat stopReason=stop with payloads=0 as ok for cron jobs that intentionally produce no output [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#76159Fetched 2026-05-03 04:41:39
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
2
Timeline (top)
commented ×1mentioned ×1subscribed ×1unsubscribed ×1

Error Message

is incremented, and exponential error-backoff kicks in. && !emptyResponseAssistant && stopReason !== "error") return null; The error then propagates through runDueJob

  • Error counter increment: `dist/server.impl-*.js:3681, 3006`

Root Cause

Cron jobs whose prompts say "if no work to do, output nothing" are marked as errors by the framework. The job runs fine, the model decides correctly that no action is needed, but because stopReason=stop with payloads.length=0 the run is classified as an "incomplete turn" — lastError: ⚠️ Agent couldn't generate a response. Please try again. is set, consecutiveErrors is incremented, and exponential error-backoff kicks in.

Fix Action

Fix / Workaround

  1. Inject a deterministic finalizer wrapper that always writes a side-effects.json file post-agent-run (extra script per cron), OR
  2. Patch every prompt with an explicit "always emit a heredoc payload" rule, even when the semantically-correct behavior is to do nothing.

Both are tactical workarounds, not fixes.

Workaround in production

Code Example

if (!incompleteTerminalAssistant && !reasoningOnlyAssistant
    && !emptyResponseAssistant && stopReason !== "error") return null;
return resolveAttemptReplayMetadata(params.attempt).hadPotentialSideEffects
    ? "⚠️ Agent couldn't generate a response. Note: some tool actions ..."
    : "⚠️ Agent couldn't generate a response. Please try again.";
RAW_BUFFERClick to expand / collapse

Problem

Cron jobs whose prompts say "if no work to do, output nothing" are marked as errors by the framework. The job runs fine, the model decides correctly that no action is needed, but because stopReason=stop with payloads.length=0 the run is classified as an "incomplete turn" — lastError: ⚠️ Agent couldn't generate a response. Please try again. is set, consecutiveErrors is incremented, and exponential error-backoff kicks in.

This forces every "silent cron" to either:

  1. Inject a deterministic finalizer wrapper that always writes a side-effects.json file post-agent-run (extra script per cron), OR
  2. Patch every prompt with an explicit "always emit a heredoc payload" rule, even when the semantically-correct behavior is to do nothing.

Both are tactical workarounds, not fixes.

Where the classification happens

dist/selection-D9uTvvsw.js:1249:

if (!incompleteTerminalAssistant && !reasoningOnlyAssistant
    && !emptyResponseAssistant && stopReason !== "error") return null;
return resolveAttemptReplayMetadata(params.attempt).hadPotentialSideEffects
    ? "⚠️ Agent couldn't generate a response. Note: some tool actions ..."
    : "⚠️ Agent couldn't generate a response. Please try again.";

isEmptyResponseAssistantTurn({ payloadCount: 0, ... }) returns true when the model emits only reasoning-blocks with no visible text and no tool calls — which is exactly what a "no work today" prompt should produce.

The error then propagates through runDueJob (dist/server.impl-*.js:3223) → consecutiveErrors increments (server.impl-*.js:3006, 3681) → backoff scheduling (server.impl-*.js:3349).

Proposed solution

Add an opt-in payload flag on cron-job definitions:

```json { "id": "my-silent-cron", "schedule": "0 6 * * *", "acceptSilentStop": true, "payload": { ... } } ```

When `acceptSilentStop === true` and the run terminates with `stopReason === "stop"` AND `payloads.length === 0` AND no tool errors, the framework should:

  • mark the run `status: "ok"`
  • reset `consecutiveErrors` to 0
  • skip the "Agent couldn't generate a response" notification
  • still record the run in `run-history.jsonl` for observability

Default remains current behavior (false) so existing crons are unaffected.

Use cases

  • Daily-briefing crons that emit nothing on quiet days
  • Heartbeat/inbox-scan jobs that legitimately have nothing to push
  • Pre-meeting-briefing that suppresses output outside the trigger window
  • Any "L1 detector → L2 reactive agent → Inbox" pipeline where the L2 agent decides not to enqueue (Pattern A)

Workaround in production

We currently ship a `_finalize.sh` wrapper next to every silent cron that writes `~/.openclaw/state/automations/<key>/side-effects.json` post-run. ~12 jobs across our deployment carry this wrapper purely to satisfy the non-empty-payload contract. A first-class `acceptSilentStop` flag would let us delete all of them.

Environment

  • OpenClaw v2026.4.26 (still present)
  • Self-hosted gateway + cron scheduler
  • Affected jobs cross-channel (Telegram + iMessage outbox)

Refs

  • Classification: `dist/selection-D9uTvvsw.js:1241-1250`
  • Incomplete-turn handling: `dist/pi-embedded-aAN5CWPb.js:2858-2876`
  • Error counter increment: `dist/server.impl-*.js:3681, 3006`
  • Backoff: `dist/server.impl-*.js:3349`

Related

  • #59765 (closed) — adjacent: empty content after think-block stripping; this request is distinct in that the cron is intended to produce no output.

extent analysis

TL;DR

The proposed solution involves adding an opt-in acceptSilentStop flag to cron-job definitions to allow the framework to correctly handle silent stops without marking them as errors.

Guidance

  • Review the proposed solution and consider implementing the acceptSilentStop flag to address the issue.
  • Verify that the flag is correctly handling silent stops by checking the run-history.jsonl file and ensuring that consecutiveErrors is reset to 0 when the flag is set to true.
  • Test the new flag with different use cases, such as daily-briefing crons and heartbeat/inbox-scan jobs, to ensure it works as expected.
  • Consider removing the _finalize.sh wrapper scripts that are currently used as a workaround, as they may no longer be necessary with the new flag.

Example

No code example is provided, as the proposed solution involves modifying the framework's behavior rather than providing a specific code snippet.

Notes

The acceptSilentStop flag is proposed as an opt-in feature, which means that existing crons will not be affected by the change. This allows for a gradual rollout of the new feature without disrupting current functionality.

Recommendation

Apply the proposed workaround by adding the acceptSilentStop flag to cron-job definitions, as it provides a more elegant and efficient solution than the current workarounds. This will allow the framework to correctly handle silent stops and reduce the need for additional wrapper scripts.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix [Feature]: Per-job acceptSilentStop flag — treat stopReason=stop with payloads=0 as ok for cron jobs that intentionally produce no output [1 comments, 2 participants]