Skip to content

fix(transcripts): close readline interface and destroy read stream on error exit#98493

Merged
vincentkoc merged 1 commit into
openclaw:mainfrom
wangmiao0668000666:fix/transcripts-store-fd-leak
Jul 1, 2026
Merged

fix(transcripts): close readline interface and destroy read stream on error exit#98493
vincentkoc merged 1 commit into
openclaw:mainfrom
wangmiao0668000666:fix/transcripts-store-fd-leak

Conversation

@wangmiao0668000666

@wangmiao0668000666 wangmiao0668000666 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

TranscriptsStore.readUtterancesFromDir (src/transcripts/store.ts:199-218) streams transcript.jsonl through a createReadStream + readline.createInterface pair when maxUtterances is set, but it never closes the interface or destroys the stream. If JSON.parse throws inside the for await loop, the function propagates the error while leaving the underlying file descriptor open. Over time this leaks fds in long-running gateway processes.

Changes

  • src/transcripts/store.ts: extract the createReadStream result into a named stream, wrap the for await loop in try/finally, and call lines.close() and stream.destroy() in finally. When stream.destroy() does not immediately close the fs handle, await the stream's close event before returning.
  • src/transcripts/store.test.ts: add regression tests for missing-file handling, normal JSONL reads, tail truncation, and fd-leak-free behavior on both the malformed-JSON error path and the happy path.

Real behavior proof

BEFORE (current main, no fix applied)
Driving the real TranscriptsStore.readUtterancesFromSessionDir against a malformed JSONL file leaks the transcript fd:

threw as expected: SyntaxError
transcript path: /tmp/openclaw-repro-98467-i9zShq/2026-07-01/session-1/transcript.jsonl
threw on malformed JSON: true
open fds before call: 25
open fds after call: 26
leaked transcript.jsonl fds: 1
  leaked fd: /tmp/openclaw-repro-98467-i9zShq/2026-07-01/session-1/transcript.jsonl
FAIL: file descriptor leak detected

NEGATIVE CONTROL
Reverting only the source change in src/transcripts/store.ts while keeping the new tests makes the fd-leak regression test fail:

× src/transcripts/store.test.ts > ... > does not leak file descriptors when JSON.parse throws
  → expected [ Array(1) ] to have a length of +0 but got 1
 Test Files  1 failed (1)
      Tests  1 failed | 4 passed (5)

AFTER (this branch)
The same local fd-count repro and the new unit tests pass with zero leaked fds:

threw as expected: SyntaxError
transcript path: /tmp/openclaw-repro-98467-TDJkNx/2026-07-01/session-1/transcript.jsonl
threw on malformed JSON: true
open fds before call: 25
open fds after call: 25
leaked transcript.jsonl fds: 0
PASS: no file descriptor leak after malformed JSON read

Evidence

  • Behavior addressed: TranscriptsStore.readUtterancesFromDir now closes the readline interface and destroys the read stream (awaiting close) when the streaming JSONL parser exits, fixing a file descriptor leak on malformed lines.
  • Real environment tested: Linux x86_64, Node v22.22.0, repo checkout /home/0668000666/0668000666/AI/OpenClaw/new_open_claw, branch fix/transcripts-store-fd-leak, base openclaw/main @ f55abc0606, live HEAD 68749382de.
  • Exact steps or command run after this patch:
    node scripts/run-vitest.mjs run src/transcripts/store.test.ts --reporter=verbose
    # → Test Files  1 passed (1)
    #      Tests  5 passed (5)
    
    # Local-only fd-count repro (not committed):
    # node --import tsx /tmp/issue-98467-fd-leak.mts
    # → leaked transcript.jsonl fds: 0
    # → PASS: no file descriptor leak after malformed JSON read
    
    node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.scripts.json \
      src/transcripts/store.ts src/transcripts/store.test.ts
    # → exit 0
    
    pnpm tsgo:core
    # → silent (no type errors)
  • Evidence after fix: 5/5 new regression tests pass; the local fd-count repro reports leaked transcript.jsonl fds: 0 after a malformed-line parse error; source change is 1 file, ~10 net LoC.
  • Observed result after fix: The streaming read path no longer leaks file descriptors when JSON.parse throws or when the loop exits normally.
  • What was not tested: Live long-running gateway telemetry under production load; the fix relies on local fd counting and unit tests. Windows/macOS fd behavior was not explicitly measured, but the cleanup calls (lines.close(), stream.destroy(), awaiting close) are platform-agnostic Node.js APIs.

Tests and validation

  • Added src/transcripts/store.test.ts with 5 tests covering:
    • missing transcript.jsonl returns []
    • normal JSONL read returns utterances
    • maxUtterances keeps only the tail
    • malformed JSON does not leak fds
    • happy path does not leak fds
  • Revert-line gate verified: reverting the try/finally cleanup makes the fd-leak test fail.

Risk checklist

  • Scope: 1 source file (src/transcripts/store.ts), 1 new test file.
  • Behavior change: only the cleanup path; happy-path output unchanged.
  • API/signature changes: none.
  • Type check: pnpm tsgo:core passes. pnpm tsgo:core:test fails on pre-existing errors in src/gateway/server-cron-notifications.test.ts, src/gateway/server-cron.test.ts, and src/llm/providers/anthropic.test.ts that are unrelated to this change (verified on openclaw/main @ f55abc0606).
  • No config/env/migration changes.
  • No security/auth/secrets/tool-execution changes.

CI status

The latest CI run on this PR shows three failing checks. All three failures are in files this PR does not touch:

  • check-lint: typescript(no-base-to-string) in src/gateway/server-cron.test.ts:626,676.
  • check-test-types: tuple-length and type mismatch errors in src/gateway/server-cron-notifications.test.ts, src/gateway/server-cron.test.ts, and src/llm/providers/anthropic.test.ts.
  • checks-node-compact-small-whole-3: assertion failure in src/llm/providers/mistral.test.ts:298.

I rebased the branch onto the latest openclaw/main @ f55abc0606; these same failures persist on main. They appear to be pre-existing CI failures unrelated to the transcript store cleanup change, so I have not expanded this PR's scope to address them.

Related

@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts size: M labels Jul 1, 2026
@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

@clawsweeper review

This PR fixes #98467 with a focused source change in src/transcripts/store.ts plus regression tests and a standalone /proc/self/fd repro script. The branch is fix/transcripts-store-fd-leak from openclaw/main @ 0a9708ed0f.

@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

PR body and branch HEAD have been updated:

  • Removed the committed repro script (scripts/repro/issue-98467-fd-leak.mts); the fd-count proof output remains in the PR body as locally-captured evidence.
  • Branch now contains only src/transcripts/store.ts and src/transcripts/store.test.ts.
  • Live HEAD: ae8fe26309 on fix/transcripts-store-fd-leak, base openclaw/main @ 0a9708ed0f.

@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@wangmiao0668000666
wangmiao0668000666 force-pushed the fix/transcripts-store-fd-leak branch from ae8fe26 to 5c8d0e3 Compare July 1, 2026 08:22
@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

Pushed 5c8d0e37c0 to address the CI findings:

  • Replaced direct fsp.mkdtemp in src/transcripts/store.test.ts with makeTempDir / cleanupTempDirs from test/helpers/temp-dir.js (synchronous, tracked cleanup).
  • node scripts/run-oxlint.mjs now passes on the changed files with no temp-dir warnings.
  • node scripts/run-vitest.mjs run src/transcripts/store.test.ts still passes 5/5.

The remaining CI failures (check-test-types, checks-node-compact-small-whole-3) are being watched; I will update once the new run finishes.

@wangmiao0668000666
wangmiao0668000666 force-pushed the fix/transcripts-store-fd-leak branch from 5c8d0e3 to 6874938 Compare July 1, 2026 08:43
@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

Rebased onto latest openclaw/main @ f55abc0606 (HEAD now 68749382de) and updated the PR body with a CI status section.

The three failing checks on the latest run are all in files this PR does not modify:

  • check-lint: src/gateway/server-cron.test.ts:626,676
  • check-test-types: src/gateway/server-cron-notifications.test.ts, src/gateway/server-cron.test.ts, src/llm/providers/anthropic.test.ts
  • checks-node-compact-small-whole-3: src/llm/providers/mistral.test.ts:298

These failures persist after rebase, so they appear to be pre-existing on main. I have not expanded the PR scope to fix unrelated files; if a maintainer prefers otherwise, let me know and I can address them in a follow-up.

@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Branch rebased onto openclaw/main @ f55abc0606. Live HEAD is 68749382de. PR body updated with CI status and pre-existing failure analysis.

@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

@maintainer team — requesting maintainer override for the three pre-existing CI failures on this PR.

All three failing checks are in files this PR does not touch:

  • check-lint: typescript(no-base-to-string) at src/gateway/server-cron.test.ts:626,676
  • check-test-types: tuple/type errors in src/gateway/server-cron-notifications.test.ts, src/gateway/server-cron.test.ts, and src/llm/providers/anthropic.test.ts
  • checks-node-compact-small-whole-3: assertion failure at src/llm/providers/mistral.test.ts:298

I rebased onto the latest openclaw/main @ f55abc0606 (live HEAD 68749382de) and the same three failures persist unchanged. I verified locally that pnpm tsgo:core:test also fails with the same server-cron/anthropic errors on a clean main checkout.

The PR scope is intentionally narrow: only src/transcripts/store.ts (+ cleanup fix) and src/transcripts/store.test.ts (+ 5 regression tests). The changed files pass their scoped checks:

  • node scripts/run-vitest.mjs run src/transcripts/store.test.ts → 5/5 passed
  • node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.scripts.json src/transcripts/store.ts src/transcripts/store.test.ts → exit 0
  • pnpm tsgo:core → silent

I would prefer not to expand this PR into unrelated gateway/LLM test files. If maintainers agree these are pre-existing main-branch failures, please consider landing this as-is; otherwise, let me know if a follow-up cleanup PR for those files is desired.

@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 1, 2026, 6:21 AM ET / 10:21 UTC.

Summary
The PR changes src/transcripts/store.ts to close the readline interface, destroy and await the read stream after capped transcript reads, and adds focused regression tests in src/transcripts/store.test.ts.

PR surface: Source +11, Tests +108. Total +119 across 2 files.

Reproducibility: yes. source inspection gives a high-confidence path: current main opens a transcript read stream in the bounded reader and can throw during JSON parsing before explicit stream cleanup. A focused Node probe also confirmed descriptor release is asynchronous unless the stream close is awaited.

Review metrics: none identified.

Stored data model
Persistent data-model change detected: serialized state: src/transcripts/store.test.ts, serialized state: src/transcripts/store.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #98467
Summary: The canonical issue reports the transcript-store fd cleanup bug; this PR is the stronger candidate fix, while the sibling PR is a competing same-root-cause branch.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Resolve or explicitly override the unrelated failing CI checks before merge.
  • After this branch lands, close or supersede the competing same-root-cause PR.

Risk before merge

  • [P1] Exact-head CI is still unstable with failures reported outside the touched transcript files, so merge needs the normal maintainer CI resolution or explicit override.
  • [P1] A competing open PR targets the same transcript fd cleanup; maintainers should close or supersede the weaker sibling once one canonical fix lands.

Maintainer options:

  1. Decide the mitigation before merge
    Land one canonical fix that closes the readline interface, destroys and awaits the read stream close, and keeps the focused Linux-gated fd regression coverage; this PR is now the better landing candidate if CI is resolved or waived.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • No automated repair remains; maintainers need to resolve or waive unrelated CI failures and select this PR as the canonical branch over the open sibling.

Security
Cleared: The diff only changes local transcript file stream cleanup and tests; it does not alter dependencies, workflows, package metadata, permissions, credentials, or secret handling.

Review details

Best possible solution:

Land one canonical fix that closes the readline interface, destroys and awaits the read stream close, and keeps the focused Linux-gated fd regression coverage; this PR is now the better landing candidate if CI is resolved or waived.

Do we have a high-confidence way to reproduce the issue?

Yes, source inspection gives a high-confidence path: current main opens a transcript read stream in the bounded reader and can throw during JSON parsing before explicit stream cleanup. A focused Node probe also confirmed descriptor release is asynchronous unless the stream close is awaited.

Is this the best way to solve the issue?

Yes. The local cleanup in TranscriptsStore is the narrowest maintainable fix; a broader refactor into shared transcript streaming helpers is not needed to fix this fd cleanup bug.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 5113fbf4cc6a.

Label changes

Label changes:

  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes before/negative/after terminal output showing the fd leak repro on current main, the revert-line test failure, and zero leaked fds after this branch.
  • remove rating: 🦐 gold shrimp: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove merge-risk: 🚨 automation: Current PR review selected no merge-risk labels.
  • remove status: ⏳ waiting on author: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P2: This is a bounded transcript resource-cleanup bugfix with limited blast radius but possible descriptor-exhaustion impact on long-running processes.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes before/negative/after terminal output showing the fd leak repro on current main, the revert-line test failure, and zero leaked fds after this branch.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes before/negative/after terminal output showing the fd leak repro on current main, the revert-line test failure, and zero leaked fds after this branch.
Evidence reviewed

PR surface:

Source +11, Tests +108. Total +119 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 19 8 +11
Tests 1 108 0 +108
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 127 8 +119

What I checked:

  • Repository policy read: Root AGENTS.md was read fully; its OpenClaw PR review policy requires whole-path source review, dependency contract proof, current-main comparison, and best-fix assessment before verdict. (AGENTS.md:1, 5113fbf4cc6a)
  • Current main source path: Current main still creates a readline interface over an inline createReadStream in the maxUtterances branch and has no local cleanup finally around the async iteration. (src/transcripts/store.ts:196, 5113fbf4cc6a)
  • PR implementation: At PR head, the capped reader names the stream, closes the readline interface, calls stream.destroy(), and awaits close when the stream is not already closed. (src/transcripts/store.ts:199, 1ecf9933b14c)
  • Caller path: Transcript stop/import/summarize flows call readUtterancesFromSessionDir or readUtterancesForSession with maxUtterances, so the touched reader is on the real transcript summary path. (src/agents/tools/transcripts-tool.ts:174, 5113fbf4cc6a)
  • Sibling cleanup pattern: The adjacent session transcript streaming helper uses the same readline plus file-stream shape and closes the interface plus destroys the stream in finally, supporting the ownership and cleanup direction. (src/config/sessions/transcript-stream.ts:52, 5113fbf4cc6a)
  • Dependency/runtime probe: Local Node v24.17.0 source shows Readable.destroy() returns the stream immediately while fs.ReadStream._destroy may wait for pending IO; a focused probe showed one transcript fd after synchronous destroy and zero after awaiting close.

Likely related people:

  • snowzlmbot: git blame and git log -S show the current TranscriptsStore bounded reader, transcript tool, docs, and sibling transcript stream helper entered this checkout through commit a68f720 authored by this account. (role: introduced current transcript-store surface; confidence: high; commits: a68f7200d888; files: src/transcripts/store.ts, src/agents/tools/transcripts-tool.ts, src/config/sessions/transcript-stream.ts)
  • obviyus: Live PR metadata for fix(cron): preserve action-required command output #96393 shows this person merged the feature-history PR, and current blame records the same person as committer on the transcript-store introduction commit. (role: merger/committer of introducing PR; confidence: high; commits: a68f7200d888; files: src/transcripts/store.ts, src/config/sessions/transcript-stream.ts)
  • jack-stormentswe: The related merged transcript-streaming PR documented the same readline/file-stream cleanup pattern and transcript JSONL streaming area, making this person useful routing context for transcript streaming behavior. (role: adjacent streaming contributor; confidence: medium; commits: 5da239473c67; files: src/config/sessions/transcript-stream.ts, src/config/sessions/transcript.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 automation 🚨 May affect CI, automerge, proof capture, label sync, or maintainer automation. labels Jul 1, 2026
@wangmiao0668000666
wangmiao0668000666 force-pushed the fix/transcripts-store-fd-leak branch from 6874938 to 1ecf993 Compare July 1, 2026 09:21
@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Addressed the P2 test-portability finding:

  • Replaced the custom listOpenFdPaths() helper with listOpenFileDescriptorsForPath from src/infra/open-file-descriptors.test-support.ts.
  • Gated the two fd-leak assertions with it.runIf(process.platform === "linux"), so the ordinary transcript read behavior tests still run on all platforms.
  • Removed the unconditional /proc/self/fd read from the test file.

Verification:

  • node scripts/run-vitest.mjs run src/transcripts/store.test.ts --reporter=verbose → 5 passed.
  • node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.scripts.json src/transcripts/store.ts src/transcripts/store.test.ts → exit 0.

Live HEAD: 1ecf9933b1 on fix/transcripts-store-fd-leak.

@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jul 1, 2026
@clawsweeper clawsweeper Bot added status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 automation 🚨 May affect CI, automerge, proof capture, label sync, or maintainer automation. labels Jul 1, 2026
@vincentkoc
vincentkoc force-pushed the fix/transcripts-store-fd-leak branch from 1ecf993 to 64bac61 Compare July 1, 2026 11:49
@vincentkoc vincentkoc self-assigned this Jul 1, 2026
@vincentkoc
vincentkoc merged commit 94cb14b into openclaw:main Jul 1, 2026
93 of 94 checks passed
@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

Thanks @vincentkoc for the quick review and merge!

openclaw-clownfish Bot pushed a commit that referenced this pull request Jul 6, 2026
…ng throws (#98840)

The batch output file download path creates a readline interface over a
Readable.fromWeb() response body stream. If JSON.parse throws on a malformed
JSONL line, the for-await loop exits via exception but the readline interface
and underlying Readable stream were never explicitly closed or destroyed,
leaving the HTTP response body stream dangling.

Extract the stream reading into , a testable helper
that wraps the iteration in a try-finally so both reader.close() and
inputStream.destroy() are always called, matching the pattern established in
#98493 for the same class of leak.
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 6, 2026
…ng throws (openclaw#98840)

The batch output file download path creates a readline interface over a
Readable.fromWeb() response body stream. If JSON.parse throws on a malformed
JSONL line, the for-await loop exits via exception but the readline interface
and underlying Readable stream were never explicitly closed or destroyed,
leaving the HTTP response body stream dangling.

Extract the stream reading into , a testable helper
that wraps the iteration in a try-finally so both reader.close() and
inputStream.destroy() are always called, matching the pattern established in
openclaw#98493 for the same class of leak.
giodl73-repo pushed a commit to giodl73-repo/openclaw that referenced this pull request Jul 8, 2026
…ng throws (openclaw#98840)

The batch output file download path creates a readline interface over a
Readable.fromWeb() response body stream. If JSON.parse throws on a malformed
JSONL line, the for-await loop exits via exception but the readline interface
and underlying Readable stream were never explicitly closed or destroyed,
leaving the HTTP response body stream dangling.

Extract the stream reading into , a testable helper
that wraps the iteration in a try-finally so both reader.close() and
inputStream.destroy() are always called, matching the pattern established in
openclaw#98493 for the same class of leak.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

transcripts: file descriptor leak in readUtterancesFromDir stream cleanup

2 participants