Skip to content

Conversation

@sauerdaniel
Copy link
Contributor

@sauerdaniel sauerdaniel commented Jan 13, 2026

Summary

Fix memory leak in State.dispose() where disposed instance keys remain in the parent Map forever.

Fixes #4315
Relates to #3013

Problem

The State namespace uses a two-level Map structure:

  • recordsByKey: Map<string, Map<any, Entry>> - outer Map keyed by instance
  • Inner Map stores state entries for that instance

When State.dispose(key) is called:

  1. It gets the entries Map for the key
  2. Calls dispose on each entry
  3. Calls entries.clear() to empty the inner Map
  4. Bug: Never deletes the key from recordsByKey

This means every disposed instance leaves an empty Map<any, Entry> in recordsByKey, causing unbounded memory growth over the application lifetime.

Solution

Add recordsByKey.delete(key) after entries.clear() to remove the outer Map entry entirely.

entries.clear()
recordsByKey.delete(key)  // <-- Added
await Promise.all(tasks)

Testing

  • ✅ Build passes for all platforms
  • ✅ Full test suite passes (652 tests, 0 failures)
  • Code-review verified to follow correct cleanup pattern

The State.dispose() function clears the entries Map but never deletes
the key from recordsByKey. This causes unbounded memory growth as each
disposed instance leaves an empty Map entry in recordsByKey forever.

Add recordsByKey.delete(key) after entries.clear() to properly clean
up the parent Map entry.
@github-actions
Copy link
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions
Copy link
Contributor

The following comment was made by an LLM, it may be inaccurate:

Based on my search, I found related PRs addressing memory leak issues in disposal patterns, but they appear to focus on different areas:

Potentially Related PRs:

  1. fix(core): add dispose functions to prevent subscription memory leaks #7032 - fix(core): add dispose functions to prevent subscription memory leaks

  2. fix(core): add dispose functions to prevent subscription memory leaks #7914 - fix(core): add dispose functions to prevent subscription memory leaks

  3. fix: clean up pending permissions when session is deleted #7154 - fix: clean up pending permissions when session is deleted

These PRs address similar memory leak patterns in disposal/cleanup scenarios, but none appear to be direct duplicates of PR #8252. PR #8252 is specifically fixing the State.dispose() memory leak by deleting keys from recordsByKey, while the related PRs focus on subscription cleanup and session cleanup in different areas of the codebase.

@sauerdaniel
Copy link
Contributor Author

Duplicate Analysis

The bot mentioned PRs #7032, #7914, and #7154 as potentially related. After investigation:

PR Files Modified Focus
#7032 agent.ts, bus/index.ts, format/index.ts, plugin/index.ts, share-next.ts, share.ts Bus subscription cleanup via dispose() functions
#7914 Same as #7032 (rebased version) Same - Bus subscription cleanup
#7154 permission/next.ts Pending permission cleanup on session delete
#8252 state.ts Map entry cleanup in State.dispose()

Verdict: Not a duplicate.

This PR fixes a different memory leak pattern in a completely different file. The State namespace maintains a recordsByKey Map that tracks state entries per project root. The existing dispose() function calls entries.clear() but never removes the root Map entry itself, causing unbounded growth over time.

The other PRs focus on Bus subscription cleanup and permission cleanup - none touch state.ts or address the Map entry retention issue fixed here.

@rekram1-node
Copy link
Collaborator

/review

@github-actions
Copy link
Contributor

lgtm

@rekram1-node rekram1-node merged commit b68a4a8 into anomalyco:dev Jan 13, 2026
5 checks passed
@rekram1-node
Copy link
Collaborator

@sauerdaniel thank you for finding these tiny details, they are hard to catch and great finds!!!

very very much appreciated and I bet youll find more.

please ping me if I miss ur prs because ur prs so far are better than most relating to mem/resource issues

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Memory stays unbounded: ACP session map + compacted tool outputs

2 participants