Summary
video_generate background tasks stay in running status for several minutes after the generated video is ready, blocking the next segment in sequential video-production workflows. Root cause is a self-deadlock in scheduleMediaGenerationTaskCompletion: the task is only marked terminal after the completion wake returns, but the wake (expectFinal: true) awaits the requester agent's full turn, and that turn is itself blocked by the still-running task via the label-less active-task prompt guard.
Net effect on a multi-segment video job: every segment pays a ~4–5 min "release tax" on top of actual generation time, because the woken turn spins retrying the next segment until it gives up and yields — only then does the task flip to succeeded.
Reproduction
Sequential video_generate calls in one session (e.g. a crew producing clip 01, 02, 03… back-to-back, each a distinct prompt). After clip N's completion event is delivered, the agent attempts clip N+1. The runtime injects an active-task warning that suppresses the new call; the agent retries/sleeps for ~4–5 min before yielding. Clip N's task record only flips running → succeeded at that point.
Evidence
From a production run on v2026.6.8 (844f405a), state/openclaw.sqlite task_runs for one session:
| task_id |
started_at |
ended_at |
running duration |
| 13b0b552 |
12:13:51 |
12:18:53 |
5m02s |
| c6043e3f |
12:54:07 |
12:59:25 |
5m18s |
| adbefdcb |
15:07:02 |
15:07:56 |
0m54s |
Provider is byteplus/doubao-seedance-1-0-pro-fast-251015, 4s clips — actual generation+download is ~60–90s. The remaining ~3.5–4m of each long row is the post-generation "task still running" window. The requester session trajectory shows the woken turn (12:54:35 → 12:59:23, 4m48s) reporting "stale state machine, 5 retries intercepted", and c6043e3f flips to succeeded at 12:59:25 — i.e. after the woken turn ended, not when the video was ready.
Root cause
src/agents/tools/media-generate-background-shared.ts, scheduleMediaGenerationTaskCompletion (line 382). Completion ordering:
397 executed = await withMediaGenerationTaskKeepalive({ run }); // byteplus generates video
418 recordTaskProgress({ progressSummary: "Generated media; delivering completion" }); // still running
431 const completionDelivered = await params.lifecycle.wakeTaskCompletion({ ... }); // expectFinal:true
487 params.lifecycle.completeTaskRun({ ... }); // ONLY HERE → succeeded
wakeTaskCompletion → deliverSubagentAnnouncement → runAnnounceAgentCall({ expectFinal: true, timeoutMs }) (src/agents/subagent-announce-delivery.ts:1454). expectFinal: true awaits the requester agent's full turn.
That woken turn, for a sequential workflow, immediately tries to start the next segment's video_generate. But the current task is still running (step at line 487 hasn't run), so resolveAttemptMediaTaskSystemPromptAddition injects:
"An active video generation background task already exists for this session. … Do not call video_generate again for the same request while that task is queued or running."
This injection is at src/agents/embedded-agent-runner/run/attempt.prompt-helpers.ts:542:
buildActiveVideoGenerationTaskPromptContextForSession(params.sessionKey),
It passes only sessionKey, no taskLabel. buildActiveMediaGenerationTaskPromptContextForSession (src/agents/media-generation-task-status-shared.ts:460) → findActiveMediaGenerationTaskForSession returns any running video task for the session regardless of prompt, so the guard suppresses every new video_generate in the session, including a clearly-different next segment.
The hard duplicate guard (createVideoGenerateDuplicateGuardResult) is label-aware and would NOT block a distinct-prompt segment — but the agent never gets there, because the label-less prompt-context warning makes it yield/retry instead.
Self-deadlock: the wake that would release the task awaits the agent turn; the agent turn can't finish because the task is still running; the task only releases when the agent gives up and yields.
Why completeTaskRun is gated behind the wake (the contract to preserve)
The current ordering exists because terminalResult is derived from completionDelivered — "a generated result without confirmed delivery is terminally unsafe for task closeout" (lines 440–452). So completeTaskRun can't simply be moved before the wake without losing that safety check.
Suggested fix
Introduce a non-blocking intermediate state between "generated" and "terminal", so the duplicate-guard / active-task prompt guard stops blocking the session as soon as the media is ready, while still deferring the final succeeded/blocked terminal outcome until delivery is confirmed:
- After
withMediaGenerationTaskKeepalive returns (video ready), mark the task e.g. delivering (or set a mediaReady/phase flag) — a state that isTaskStillBlockingDuplicateGuard and buildActiveMediaGenerationTaskPromptContextForSession treat as non-blocking for new, distinct requests.
- Run
wakeTaskCompletion (still expectFinal: true).
- Then
completeTaskRun with the terminalResult derived from completionDelivered as today.
Alternative (narrower): make buildActiveVideoGenerationTaskPromptContextForSession label-scoped or have it skip tasks whose progressSummary indicates "delivering completion", so a distinct-prompt next segment isn't suppressed. This is lower-risk but more of a string-match hack.
Environment
- OpenClaw v2026.6.8 (commit
844f405ac1be805d5c598922a37254f12ab6d765)
- Provider:
byteplus (doubao-seedance, Volcengine ARK endpoint)
- Runtime: Linux, gateway systemd unit, SQLite state store
Workaround in use
Instructing the crew's skill to ignore the active-task warning for distinct segments and call video_generate anyway (the hard guard already permits distinct prompts). This unblocks the workflow but leaves the task record overstaying running and allows concurrent segment generation, so it's a workaround, not a fix.
Summary
video_generatebackground tasks stay inrunningstatus for several minutes after the generated video is ready, blocking the next segment in sequential video-production workflows. Root cause is a self-deadlock inscheduleMediaGenerationTaskCompletion: the task is only marked terminal after the completion wake returns, but the wake (expectFinal: true) awaits the requester agent's full turn, and that turn is itself blocked by the still-runningtask via the label-less active-task prompt guard.Net effect on a multi-segment video job: every segment pays a ~4–5 min "release tax" on top of actual generation time, because the woken turn spins retrying the next segment until it gives up and yields — only then does the task flip to
succeeded.Reproduction
Sequential
video_generatecalls in one session (e.g. a crew producing clip 01, 02, 03… back-to-back, each a distinct prompt). After clip N's completion event is delivered, the agent attempts clip N+1. The runtime injects an active-task warning that suppresses the new call; the agent retries/sleeps for ~4–5 min before yielding. Clip N's task record only flipsrunning → succeededat that point.Evidence
From a production run on v2026.6.8 (
844f405a),state/openclaw.sqlitetask_runsfor one session:Provider is
byteplus/doubao-seedance-1-0-pro-fast-251015, 4s clips — actual generation+download is ~60–90s. The remaining ~3.5–4m of each long row is the post-generation "task still running" window. The requester session trajectory shows the woken turn (12:54:35 → 12:59:23, 4m48s) reporting "stale state machine, 5 retries intercepted", andc6043e3fflips tosucceededat 12:59:25 — i.e. after the woken turn ended, not when the video was ready.Root cause
src/agents/tools/media-generate-background-shared.ts,scheduleMediaGenerationTaskCompletion(line 382). Completion ordering:wakeTaskCompletion→deliverSubagentAnnouncement→runAnnounceAgentCall({ expectFinal: true, timeoutMs })(src/agents/subagent-announce-delivery.ts:1454).expectFinal: trueawaits the requester agent's full turn.That woken turn, for a sequential workflow, immediately tries to start the next segment's
video_generate. But the current task is stillrunning(step at line 487 hasn't run), soresolveAttemptMediaTaskSystemPromptAdditioninjects:This injection is at
src/agents/embedded-agent-runner/run/attempt.prompt-helpers.ts:542:It passes only
sessionKey, notaskLabel.buildActiveMediaGenerationTaskPromptContextForSession(src/agents/media-generation-task-status-shared.ts:460) →findActiveMediaGenerationTaskForSessionreturns any running video task for the session regardless of prompt, so the guard suppresses every newvideo_generatein the session, including a clearly-different next segment.The hard duplicate guard (
createVideoGenerateDuplicateGuardResult) is label-aware and would NOT block a distinct-prompt segment — but the agent never gets there, because the label-less prompt-context warning makes it yield/retry instead.Self-deadlock: the wake that would release the task awaits the agent turn; the agent turn can't finish because the task is still running; the task only releases when the agent gives up and yields.
Why
completeTaskRunis gated behind the wake (the contract to preserve)The current ordering exists because
terminalResultis derived fromcompletionDelivered— "a generated result without confirmed delivery is terminally unsafe for task closeout" (lines 440–452). SocompleteTaskRuncan't simply be moved before the wake without losing that safety check.Suggested fix
Introduce a non-blocking intermediate state between "generated" and "terminal", so the duplicate-guard / active-task prompt guard stops blocking the session as soon as the media is ready, while still deferring the final
succeeded/blockedterminal outcome until delivery is confirmed:withMediaGenerationTaskKeepalivereturns (video ready), mark the task e.g.delivering(or set amediaReady/phaseflag) — a state thatisTaskStillBlockingDuplicateGuardandbuildActiveMediaGenerationTaskPromptContextForSessiontreat as non-blocking for new, distinct requests.wakeTaskCompletion(stillexpectFinal: true).completeTaskRunwith theterminalResultderived fromcompletionDeliveredas today.Alternative (narrower): make
buildActiveVideoGenerationTaskPromptContextForSessionlabel-scoped or have it skip tasks whoseprogressSummaryindicates "delivering completion", so a distinct-prompt next segment isn't suppressed. This is lower-risk but more of a string-match hack.Environment
844f405ac1be805d5c598922a37254f12ab6d765)byteplus(doubao-seedance, Volcengine ARK endpoint)Workaround in use
Instructing the crew's skill to ignore the active-task warning for distinct segments and call
video_generateanyway (the hard guard already permits distinct prompts). This unblocks the workflow but leaves the task record overstayingrunningand allows concurrent segment generation, so it's a workaround, not a fix.