Skip to content

[Bug]: Claude workflow progress re-stores the whole workflow.agents array on every subagent.updated (1.4 GB for one 486-agent workflow, never compacted) #27598

Closed as not planned

Description

@mahirozdin

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. On 0.1.3 with the Claude provider, start a thread whose agent runs a Claude Workflow that fans out to many agents (here: one workflow with 486 agents over ~28 h, another with 314).
  2. Let it run. Every workflow progress tick produces a subagent.updated event.
  3. Open statev2.sqlite read-only and measure the stored bytes per workflow subagent:
SELECT count(*)                                             AS updates,
       round(sum(length(payload_json)) / 1048576.0)          AS stored_mib,
       round(max(length(payload_json)) / 1024.0)             AS last_kib,
       json_array_length(payload_json, '$.workflow.agents')  AS agents
FROM orchestration_events
WHERE event_type = 'subagent.updated'
  AND json_type(payload_json, '$.workflow.agents') = 'array'
GROUP BY json_extract(payload_json, '$.id')
ORDER BY stored_mib DESC
LIMIT 5;

Expected behavior

The event store grows roughly in proportion to what changed. A tick that changes one workflow member stores that member (or a small delta), not the whole member list again. Superseded subagent.updated snapshots are compacted like node.updated and message.updated.

Actual behavior

Each subagent.updated for a workflow subagent stores the complete workflow.agents array. Each member carries its own prompt, result, childThreadId, timings and token counts. Between two consecutive updates, on average 1.04 members change (max 2; 1 of 4,470 updates changed nothing). The array grows as agents are queued, so total stored bytes grow roughly quadratically with workflow size.

Results from the query above on one install (aggregate numbers only):

updates stored last snapshot agents
4,461 1,422 MiB 594 KiB 486
2,829 585 MiB 385 KiB 314
276 24 MiB 120 KiB 102
246 11 MiB 83 KiB 68
103 8 MiB 96 KiB 81
  • The largest workflow stored 1,422 MiB for a final state of 594 KiB, about 2,400×.
  • Over the last ~36 h, subagent.updated was 2.07 GB, and 98.9 % of it is $.workflow.agents. Inside that array, the members' prompt fields are the largest part, followed by childThreadId and result.
  • Over 7 days, by event type (count, payload MiB): subagent.updated 43,654 / 2,595; turn-item.updated 260,695 / 682; node.updated 183,693 / 220. Everything else is under 50 MiB.
  • Daily orchestration_events payload went from ~200–350 MiB/day to 1,805 MiB on the day these workflows ran. statev2.sqlite is now 10.0 GB, with orchestration_events at 6.95 GB. auto_vacuum is 0 and the freelist is 0.
  • No compaction happens. History compaction (fix(server): compact superseded history in the background #19435) keeps the first and newest rows only for node.updated and message.updated, so every workflow snapshot stays permanently. The corresponding rows in orchestration_v2_projection_subagents are small (276 rows, 13 MiB), so only the event log is affected.

Related but different: #18204 removed the repeated top-level subagent prompt (fixed by #18375). The per-member prompt/result inside workflow.agents is still re-stored on every tick. Retention and compaction in general: #21371, #24789, #18559.

Impact

Major degradation or frequent failure

Version or commit

t3 v0.1.3

Environment

Ubuntu 26.04.1 LTS (kernel 7.0), Node v22.22.1, shared headless dev server, Claude provider (claudeAgent driver) running Workflow fan-outs.

Logs or stack traces

# subagent.updated, last ~150k events (2026-10-28 18:00 → 2026-10-30 13:00 UTC)
events: 12,141   payload: 2,069.7 MiB
  $.workflow.agents   2,097.8 MiB  98.9 %
  $.workflow.phases       7.8 MiB   0.4 %
  everything else        < 3 MiB each
workflow subagents: 17, events: 9,003
changed agents per update (largest workflow): mean 1.04, max 2

Workaround

None in-product. Compaction does not touch subagent.updated, so these rows stay. Avoiding very large Workflow fan-outs on long-lived servers limits the growth. The disk space is only reclaimed by deleting the rows and running an offline VACUUM (t3 doctor).

Activity

  1. juliusmarminge commented on Nov 12, 2026

    @juliusmarminge
    Member

    I tried to reproduce this on the current build and could not.

    Commit ed54f30 (origin/main). Not seen in a running app: that needs the Claude provider running a real Workflow fan-out, and no worker has a Claude provider (a large Workflow would also cost real money). Reproduced through the production OrchestrationEventStore layer on in-memory SQLite instead: added the test 'stores a workflow tick without re-storing its unchanged members' to apps/server/src/persistence/OrchestrationEventStore.test.ts. It appends 60 subagent.updated events for one workflow subagent with 60 members (each member has a roughly 1 KB prompt), and each tick completes one more member, matching the reporter's measured 1.04 changed members per tick. Stored payload_json totals 3,328,029 bytes for a final snapshot of about 56 KB, roughly 60 times. Total storage grows quadratically with member count, as the reporter measured (1,422 MiB for a 594 KiB final state). Replay decodes correctly. vp test run src/persistence/OrchestrationEventStore.test.ts: the new test fails ('expected 3328029 to be below 168258') and the other 10 pass. The 'never compacted' half is no longer true on main: #29806 (merged 2026-11-03, after the reporter's 0.1.3) prunes events older than about 24 to 30 h, keeping stream heads. The per-tick full-array write and up to a day of it on disk remain.

    Closing as not planned. If it still happens for you, reply with the exact steps and the version you are on and it will be looked at again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions