Skip to content

tool-output spill files are not cleaned up and can consume tens of GB #29694

Closed as not planned

Description

@Edison-A-N

Description

Large truncated tool-output spill files under ~/.local/share/opencode/tool-output appear to accumulate without automatic cleanup and can consume a large amount of disk space.

On my machine this directory reached 63G. The largest files were single tool_* spill files in the multi-GB range, for example:

63G  ~/.local/share/opencode/tool-output
9.0G ~/.local/share/opencode/tool-output/tool_<redacted>
8.6G ~/.local/share/opencode/tool-output/tool_<redacted>
7.7G ~/.local/share/opencode/tool-output/tool_<redacted>
4.5G ~/.local/share/opencode/tool-output/tool_<redacted>
4.3G ~/.local/share/opencode/tool-output/tool_<redacted>

These files look like full tool outputs written when the normal tool response is truncated. It is useful to keep them temporarily, but keeping them indefinitely can silently fill the user's disk.

While looking at the current source, I noticed that packages/opencode/src/tool/truncate.ts defines a cleanup path with a 7 day retention:

const RETENTION = Duration.days(7)

const cleanup = Effect.fn("Truncate.cleanup")(function* () {
  const cutoff = Identifier.timestamp(
    Identifier.create("tool", "ascending", Date.now() - Duration.toMillis(RETENTION)),
  )
  const entries = yield* fs.readDirectory(TRUNCATION_DIR).pipe(
    Effect.map((all) => all.filter((name) => name.startsWith("tool_"))),
    Effect.catch(() => Effect.succeed([])),
  )
  for (const entry of entries) {
    if (Identifier.timestamp(entry) >= cutoff) continue
    yield* fs.remove(path.join(TRUNCATION_DIR, entry)).pipe(Effect.catch(() => Effect.void))
  }
})

However, I could not find a caller for this cleanup function with a search like:

rg "Truncate.*cleanup|\.cleanup\(\)|compaction.*prune|prune" packages/opencode/src -n

So the cleanup logic may exist but not be wired into startup/session lifecycle/tool-output writes.

Expected behavior:

  • Old ~/.local/share/opencode/tool-output/tool_* spill files should be pruned automatically, or
  • OpenCode should expose a cleanup/prune command for this directory, or
  • OpenCode should document that users need to manually clean this directory.

Current workaround:

find ~/.local/share/opencode/tool-output -type f -name 'tool_*' -mtime +7 -delete

or, if old full tool outputs are not needed:

rm -rf ~/.local/share/opencode/tool-output/*

Plugins

Not provided.

OpenCode version

1.15.11

Steps to reproduce

  1. Use OpenCode for sessions that produce large tool outputs which exceed the truncation limit and are saved as full output files.

  2. Check the spill directory size:

    du -sh ~/.local/share/opencode/tool-output
  3. Inspect the largest spill files:

    du -ah ~/.local/share/opencode/tool-output | sort -hr | head -30
  4. Observe that old tool_* files remain and can grow to many GB.

Screenshot and/or share link

Not attached. The relevant output is included above with local paths and file names redacted.

Operating System

Ubuntu 20.04 based Linux, x86_64 (Linux 5.15.0-139-generic).

Terminal

Not terminal-specific.

Activity

  1. lexlian commented on May 29, 2026

    @lexlian

    The cleanup mechanism is correctly implemented and working. Here's the source code:

    Scheduled cleanup pipeline (truncate.ts:149-159):

    yield* cleanup().pipe(
      Effect.flatMap(() => Effect.sync(() => log.info("tool-output cleanup running"))),
      Effect.catchCause((cause) => {
        log.error("truncation cleanup failed", { cause: Cause.pretty(cause) })
        return Effect.void
      }),
      Effect.repeat(Schedule.spaced(Duration.hours(1))),
      Effect.delay(Duration.minutes(1)),
      Effect.forkScoped,
    )

    What the pipeline does: The cleanup fiber is forked via forkScoped at TUI startup, waits 1 minute, runs the cleanup (deleting files older than 7 days), then repeats every hour.

    The forkScoped concern: forkScoped ties the fiber's lifetime to the runtime's scope. If that scope closes before the first cleanup fires (worker restart at T+30s, crash recovery, auto-update mid-session), the fiber is interrupted and that session contributes zero cleanups. forkScoped is correct for normal sessions but could be fragile under process disruption.

    Why accumulation may occur: Accumulation happens when the fiber is interrupted before T+60s in sessions that generate spill files. Each restart resets the schedule. This is a scenario-level issue, not a code defect.

  2. chubes4 commented on Jul 28, 2026

    @chubes4
    Contributor

    Additional macOS production evidence from 2026-07-28: ~/.local/share/opencode/tool-output reached 33 GiB with 6,600+ spill entries. No owning cleanup command surfaced; deleting the spill files manually reduced the OpenCode store from 97 GiB to 65 GiB. The files were independent of the 64 GiB SQLite database. Please include startup/periodic orphan reconciliation, active-session protection, aggregate byte limits, and a user-facing opencode storage cleanup --dry-run/--apply path in acceptance.

  3. github-actions commented on Sep 27, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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

Metadata

Metadata

Assignees

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