René's URL Explorer Experiment


Title: Worker drops its iii connection when an endpoint response exceeds 16 MiB (worker→engine frame cap); semantic/insights provenance lists are unbounded · Issue #1579 · rohitg00/agentmemory · GitHub

Open Graph Title: Worker drops its iii connection when an endpoint response exceeds 16 MiB (worker→engine frame cap); semantic/insights provenance lists are unbounded · Issue #1579 · rohitg00/agentmemory

X Title: Worker drops its iii connection when an endpoint response exceeds 16 MiB (worker→engine frame cap); semantic/insights provenance lists are unbounded · Issue #1579 · rohitg00/agentmemory

Description: Summary Once a state scope grows past 16 MiB, any HTTP endpoint that returns the whole scope (e.g. GET /agentmemory/semantic) makes the worker drop its connection to iii-engine. The worker then reconnects, and every endpoint returns 404/...

Open Graph Description: Summary Once a state scope grows past 16 MiB, any HTTP endpoint that returns the whole scope (e.g. GET /agentmemory/semantic) makes the worker drop its connection to iii-engine. The worker then rec...

X Description: Summary Once a state scope grows past 16 MiB, any HTTP endpoint that returns the whole scope (e.g. GET /agentmemory/semantic) makes the worker drop its connection to iii-engine. The worker then rec...

Opengraph URL: https://github.com/rohitg00/agentmemory/issues/1579

X: @github

direct link

Domain: github.com


Hey, it has json ld scripts:
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Worker drops its iii connection when an endpoint response exceeds 16 MiB (worker→engine frame cap); semantic/insights provenance lists are unbounded","articleBody":"## Summary\n\nOnce a state scope grows past **16 MiB**, any HTTP endpoint that returns the whole scope (e.g. `GET /agentmemory/semantic`) makes the **worker drop its connection to iii-engine**. The worker then reconnects, and every endpoint returns 404/500 until it is back. With a dashboard polling, this becomes a reconnect loop: we measured **280 reconnects in 10 minutes**.\n\nThe usual reading is \"the worker↔engine frame is capped at 16 MiB and not configurable\", which leads to purging big scopes. We measured both directions, and the cap only applies to **messages the worker sends to the engine**. Messages the engine sends to the worker go through up to ~100 MiB. Internal `kv.list()` calls are therefore fine. What breaks is an **endpoint response** that embeds a whole scope.\n\nTwo unbounded provenance lists make scopes reach that size quickly. Details and proposed fixes below.\n\n## Environment\n\n- `@agentmemory/agentmemory` **0.9.29** (global install, server = `dist/cli.mjs` → `dist/src-\u003chash>.mjs`)\n- `iii` engine **0.11.2**, `iii-sdk` 0.11.2 (bundled), state adapter `kv` / `file_based`\n- Node 22, Linux x86_64\n\nStore sizes at the time: `mem:insights` 159 MB, `mem:semantic` 34.8 MB, `mem:audit` 29.3 MB.\n\n## Reproduction (both directions, against a live engine, using a throwaway scope)\n\n```js\n// repro.mjs — run with: node repro.mjs \u003cmode>\nimport { registerWorker } from \"\u003cagentmemory>/node_modules/iii-sdk/dist/index.mjs\";\nconst mode = process.argv[2];\nconst sdk = registerWorker(\"ws://127.0.0.1:49134\", { workerName: \"repro-\" + mode, invocationTimeoutMs: 60000 });\nconst t0 = Date.now();\nconst done = (m) => { console.log(mode, m, Date.now() - t0 + \"ms\"); process.exit(0); };\ntry {\n  if (mode === \"list\") {                       // engine -> worker\n    const r = await sdk.trigger({ function_id: \"state::list\", payload: { scope: \"mem:audit\" } });\n    done(\"OK bytes=\" + JSON.stringify(r).length);\n  } else {                                     // worker -> engine, e.g. mode \"set-17\"\n    const n = Number(mode.split(\"-\")[1]) * 1024 * 1024;\n    await sdk.trigger({ function_id: \"state::set\", payload: { scope: \"repro:frame\", key: \"k\", value: { x: \"a\".repeat(n) } } });\n    await sdk.trigger({ function_id: \"state::delete\", payload: { scope: \"repro:frame\", key: \"k\" } });\n    done(\"OK\");\n  }\n} catch (e) { done(\"ERROR \" + (e?.message ?? e)); }\n```\n\nResults:\n\n| Direction | Payload | Result |\n|---|---|---|\n| engine → worker, `state::list` of `mem:audit` | 27.7 MB (66,381 items) | **OK**, 0.9 s |\n| engine → worker, `state::list` of a test scope | 84 MiB | **OK**, 0.4 s |\n| engine → worker, `state::list` of a test scope | 112 MiB | **fails**: `Invocation timeout after 90000ms` |\n| worker → engine, `state::set` | 15 MiB | **OK** |\n| worker → engine, `state::set` | 17 MiB | **fails**: connection dropped silently, `Invocation timeout after 60000ms` |\n\nThe running agentmemory worker was not affected by these tests. Only the throwaway worker's own connection dropped.\n\n## Root cause\n\n- **worker → engine:** the engine serves the worker WebSocket through axum (`engine/src/workers/worker/ws_handler.rs`). This looks like tungstenite's default `max_frame_size` of 16 MiB. The Node `ws` client sends each message as a single frame, so any message over 16 MiB is rejected, the socket closes, and the pending invocation times out. Nothing in `iii-config.yaml` exposes this setting.\n- **engine → worker:** the Node `ws` client uses its default `maxPayload` of 100 MiB (`iii-sdk` does not override it). Reads up to that size succeed.\n- **What actually sends > 16 MiB:** an HTTP handler such as `api::semantic-list` returns `{ semantic: await kv.list(KV.semantic) }`. That return value travels **worker → engine** (to iii-http), so it hits the cap. The same applies to the export endpoint, which returns memories, actions, semantic, procedural, relations and graph together. 130 `api::*` handlers are registered, and several return whole scopes.\n\n## Why scopes get that big: two unbounded provenance lists\n\n1. **`semantic.sourceSessionIds`**, in `mem::consolidate-pipeline`:\n   ```js\n   sourceSessionIds: recentSummaries.map((s) => s.sessionId),\n   ```\n   `recentSummaries` is the latest batch of up to 20 session summaries. **The same 20 ids are copied into every new fact** of that pass, so this is not real per-fact provenance. In our store this field was **69 % of `mem:semantic`'s bytes**.\n2. **`insights.sourceMemoryIds`**, in `mem::reflect`:\n   ```js\n   sourceMemoryIds: cluster.factIds,\n   ```\n   This holds every semantic fact sharing a term with the cluster and is never capped (same for `sourceLessonIds` / `sourceCrystalIds`). `mem:insights` reached **159 MB**.\n   Also, `insight-decay-sweep` only rewrites insights whose confidence changed. Insights at the floor, or soft-deleted, are never rewritten, so bounding at write time alone would not shrink the existing scope.\n\nAs far as we can tell, nothing in the 0.9.29 bundle reads these fields back, except export/import and mesh sync, which copy them as-is. When we checked the 0.9.30 tarball, both assignments were unchanged. `boundSources` (`slice(-32)`) in 0.9.30 only applies to graph `sourceObservationIds`.\n\n## What we deployed locally (works, measured)\n\n**1. Paginate `api::*` responses above a byte budget.** We wrap `sdk.registerFunction` once, right after `registerWorker`:\n\n```js\n{\n  const budget = Number(process.env.AGENTMEMORY_REPONSE_MAX_OCTETS) || 12 * 1024 * 1024;\n  const size = (v) => Buffer.byteLength(JSON.stringify(v) ?? \"\");\n  const int = (v) => { const n = Number.parseInt(v, 10); return Number.isFinite(n) && n >= 0 ? n : null; };\n  const paginate = (req, res) => {\n    if (!res?.body || typeof res.body !== \"object\" || Array.isArray(res.body)) return res;\n    const q = req?.query_params || {};\n    const asked = int(q.offset) !== null || int(q.limit) !== null;\n    const total = size(res.body);\n    if (!asked && total \u003c= budget) return res;\n    const arrays = Object.keys(res.body).filter((k) => Array.isArray(res.body[k]))\n      .map((k) => [k, size(res.body[k])]).sort((a, b) => b[1] - a[1]);\n    if (!arrays.length) return res;\n    const offset = int(q.offset) ?? 0;\n    const [bigName, bigBytes] = arrays[0];\n    let limit = int(q.limit) || Math.max(1, Math.floor((budget * 0.9 - (total - bigBytes)) / (bigBytes / (res.body[bigName].length || 1))));\n    const orig = Object.fromEntries(arrays.map(([k]) => [k, res.body[k]]));\n    let fields;\n    for (;;) {\n      fields = [];\n      for (const [k] of arrays) {\n        res.body[k] = orig[k];\n        if (!asked && size(res.body) \u003c= budget) break;\n        res.body[k] = orig[k].slice(offset, offset + limit);\n        fields.push(k);\n      }\n      if (size(res.body) \u003c= budget || limit === 1) break;\n      limit = Math.max(1, Math.floor(limit / 2));\n    }\n    const totals = Object.fromEntries(fields.map((k) => [k, orig[k].length]));\n    const more = fields.some((k) => offset + limit \u003c orig[k].length);\n    res.body.pagination = { fields, offset, limit, totals, next_offset: more ? offset + limit : null };\n    return res;\n  };\n  const register = sdk.registerFunction.bind(sdk);\n  sdk.registerFunction = (id, handler, ...rest) => {\n    if (typeof id !== \"string\" || !id.startsWith(\"api::\") || typeof handler !== \"function\") return register(id, handler, ...rest);\n    return register(id, async (req, ...a) => {\n      const res = await handler(req, ...a);\n      try { return paginate(req, res); } catch { return res; }\n    }, ...rest);\n  };\n}\n```\n\n- The response shape is unchanged below the budget.\n- Above it, the largest arrays are paged with `?offset=&limit=` and the body carries `pagination.next_offset`.\n- A client that ignores pagination gets the first page instead of taking the worker down.\n\nMeasured in production: `GET /agentmemory/semantic` (15.7 MB) is served as 2 pages (20,861 + 8,087 facts). All 28,948 facts were rebuilt with no duplicates, in 1.8 s, with 0 worker reconnects. The effective per-scope ceiling moves from 16 MiB to ~100 MiB, the engine → worker limit.\n\n**2. Bound the provenance lists at the single `state::set` choke point** (`StateKV.set`):\n\n- `mem:semantic`: keep the 3 most recent `sourceSessionIds` and add `sourceSessionCount` with the original length.\n- `mem:insights`: keep the last 32 of `sourceMemoryIds` / `sourceLessonIds` / `sourceCrystalIds`.\n\nThe decay tier of `consolidate-pipeline` rewrites every semantic fact on each pass. The existing scope therefore shrank by itself within one pass: **34.8 MB → 16.5 MB**, 28,956 facts rewritten, 0 errors. `mem:insights` had to be purged once (service stopped, file backed up). It regenerated to 100 KB.\n\n## Suggested upstream fixes\n\n1. **Paginate list endpoints** (`/agentmemory/semantic`, `/memories`, export, …): add `offset`/`limit` (or a cursor) and return `next_offset`, at least whenever the serialized body would exceed a budget below the frame cap. The viewer would need to follow `next_offset`.\n2. **Do not copy the batch's session ids into every semantic fact.** Either record the sessions a fact actually came from, or store a count. If the list stays, cap it the way `boundSources` caps graph sources.\n3. **Cap `insights.source*Ids`** (`slice(-32)`, like `boundSources`). Also give soft-deleted and floor-confidence insights a hard-delete or compaction path; today nothing ever rewrites or removes them.\n4. **Engine/SDK side, optional:** expose the WebSocket `max_frame_size` / `max_message_size` in the iii config, or have the SDK fragment large messages. Tungstenite reassembles fragmented messages up to `max_message_size`, which defaults to 64 MiB. Either way, failing loudly instead of a silent drop and a 60 s timeout would have saved us a lot of diagnosis.\n\nHappy to open a PR for (1)–(3) against the TypeScript sources if that direction suits you.\n","author":{"url":"https://github.com/Fguarien","@type":"Person","name":"Fguarien"},"datePublished":"2026-10-17T19:46:48Z","url":"https://github.com/rohitg00/agentmemory/issues/1579"}

react-profiling0
react-import-mapreact,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf
route-pattern/_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format)
route-controllervoltron_issues_fragments
route-actionissue_layout
fetch-noncev2:998a08ec-34e9-71ba-e1fa-b02ee5ea25aa
current-catalog-service-hash81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114
request-idD018:161404:AD765:DFD9C:6AEBDF3D
html-safe-noncefc06af27020e8c234c50bc29ce709409732683ebd5e833cb729e2e33d9bfbf39
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJEMDE4OjE2MTQwNDpBRDc2NTpERkQ5Qzo2QUVCREYzRCIsInZpc2l0b3JfaWQiOiIxODc0ODU3OTMzNTA1NTUyMTg5IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0=
visitor-hmac1a9ec7bc8d792ca6088616183cff1cfe809c10dbe59fa1ae626ff54b7a0be901
hovercard-subject-tagissue:5902409936
github-keyboard-shortcutsrepository,issues,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///voltron/issues_fragments/issue_layout
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/rohitg00/agentmemory/1579/issue_layout
twitter:imagehttps://opengraph.githubassets.com/fb47c2126a046bb4aa89bcf8a225c6ed0de66396d5b310216d01aca3ad05bf17/rohitg00/agentmemory/issues/1579
twitter:cardsummary_large_image
og:imagehttps://opengraph.githubassets.com/fb47c2126a046bb4aa89bcf8a225c6ed0de66396d5b310216d01aca3ad05bf17/rohitg00/agentmemory/issues/1579
og:image:altSummary Once a state scope grows past 16 MiB, any HTTP endpoint that returns the whole scope (e.g. GET /agentmemory/semantic) makes the worker drop its connection to iii-engine. The worker then rec...
og:image:width1200
og:image:height600
og:site_nameGitHub
og:typeobject
og:author:usernameFguarien
hostnamegithub.com
expected-hostnamegithub.com
None701f7e3aecc56e7594e73fa7b7e1483af74322a67f7038be453d0feb05e86e77
turbo-cache-controlno-preview
go-importgithub.com/rohitg00/agentmemory git https://github.com/rohitg00/agentmemory.git
octolytics-dimension-user_id48523873
octolytics-dimension-user_loginrohitg00
octolytics-dimension-repository_id1166408297
octolytics-dimension-repository_nworohitg00/agentmemory
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id1166408297
octolytics-dimension-repository_network_root_nworohitg00/agentmemory
turbo-body-classeslogged-out env-production page-responsive
disable-turbofalse
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release12d1048a3491dd63e4193b2937cf72f1c24a2a52
ui-targetfull
theme-color#1e2327
color-schemelight dark
react-app-nameissues

Links:

Skip to contenthttps://github.com/rohitg00/agentmemory/issues/1579#start-of-content
https://github.com/
Sign inhttps://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Frohitg00%2Fagentmemory%2Fissues%2F1579
GitHubRepository hosting, code review, and collaborationhttps://github.com/platform
CopilotAI coding agent for tasks, projects and automated workhttps://github.com/features/copilot
ActionsCI/CD and automationhttps://github.com/features/actions
Code QualityQuality analysis, fixes, and enforcementhttps://github.com/features/code-quality
CodespacesCloud development environmentshttps://github.com/features/codespaces
Code SecurityVulnerability detection and remediationhttps://github.com/security/advanced-security/code-security
Secret ProtectionSecret scanning and leak preventionhttps://github.com/security/advanced-security/secret-protection
All Featureshttps://github.com/features
Documentationhttps://docs.github.com/en
Traininghttps://learn.github.com/skills
Certificationshttps://learn.github.com/credentials
Build with Copilothttps://github.com/features/ai
Customize Copilothttps://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/customize-copilot-overview
Integrationshttps://github.com/marketplace
Trending reposhttps://github.com/trending
GitHub Sponsorshttps://github.com/open-source/sponsors
Developer articleshttps://github.com/resources/articles
ChangelogYour source for the latest features, improvements, and fixes across GitHub products, all in one place.https://github.blog/changelog/
Enterpriseshttps://github.com/enterprise
Teamhttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
See all industrieshttps://github.com/solutions/industry
Why GitHubhttps://github.com/why-github
Customer Storieshttps://github.com/customer-stories
Business insightshttps://github.com/solutions/executive-insights
Trust Centerhttps://github.com/trust-center
GitHub Advanced SecurityEnterprise-grade code securityhttps://github.com/security/advanced-security
Copilot for businessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/enterprise/premium-support
Documentationhttps://docs.github.com/en
Resource centerhttps://github.com/resources
Bloghttps://github.blog/
Events & webinarshttps://github.com/resources/events
Community Forumhttps://github.com/orgs/community/discussions
Customer Storieshttps://github.com/customer-stories
Integrationshttps://github.com/marketplace
Customer supporthttps://help.github.com/
Traininghttps://learn.github.com/skills
GitHub Statushttps://www.githubstatus.com/
Abouthttps://github.com/about
Partnershttps://github.com/partners
Changeloghttps://github.blog/changelog/
GitHub UniverseThe event for developers, by developers — talks, workshops, and what’s next for GitHub.https://githubuniverse.com/
Pricinghttps://github.com/pricing
Sign inhttps://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Frohitg00%2Fagentmemory%2Fissues%2F1579
Sign uphttps://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fvoltron%2Fissues_fragments%2Fissue_layout&source=header-repo&source_repo=rohitg00%2Fagentmemory
Reloadhttps://github.com/rohitg00/agentmemory/issues/1579
Reloadhttps://github.com/rohitg00/agentmemory/issues/1579
Reloadhttps://github.com/rohitg00/agentmemory/issues/1579
rohitg00https://github.com/rohitg00
agentmemoryhttps://github.com/rohitg00/agentmemory
Codehttps://github.com/rohitg00/agentmemory
Issues238 (238)https://github.com/rohitg00/agentmemory/issues
Pull requests252 (252)https://github.com/rohitg00/agentmemory/pulls
Discussionshttps://github.com/rohitg00/agentmemory/discussions
Actionshttps://github.com/rohitg00/agentmemory/actions
Projectshttps://github.com/rohitg00/agentmemory/projects
Security and qualityhttps://github.com/rohitg00/agentmemory/security
Insightshttps://github.com/rohitg00/agentmemory/pulse
https://github.com/rohitg00/agentmemory
New issuehttps://github.com/login?return_to=https://github.com/rohitg00/agentmemory/issues/1579
New issuehttps://github.com/login?return_to=https://github.com/rohitg00/agentmemory/issues/1579
Worker drops its iii connection when an endpoint response exceeds 16 MiB (worker→engine frame cap); semantic/insights provenance lists are unboundedhttps://github.com/rohitg00/agentmemory/issues/1579#top
https://github.com/Fguarien
Fguarienhttps://github.com/Fguarien
on Oct 17, 2026https://github.com/rohitg00/agentmemory/issues/1579#issue-5902409936
https://github.com/onelaview
onelaviewhttps://github.com/onelaview
on Oct 18, 2026https://github.com/rohitg00/agentmemory/issues/1579#issuecomment-6246446410
https://github.com/Fguarien
Fguarienhttps://github.com/Fguarien
on Nov 1, 2026https://github.com/rohitg00/agentmemory/issues/1579#issuecomment-6558418757
#1422https://github.com/rohitg00/agentmemory/pull/1422
#1143https://github.com/rohitg00/agentmemory/pull/1143
#1447https://github.com/rohitg00/agentmemory/pull/1447
Fguarienhttps://github.com/Fguarien
on Nov 1, 2026https://github.com/rohitg00/agentmemory/issues/1579#event-34833423773
fix(sessions): read summaries by key for returned sessions onlyhttps://github.com/Fguarien/agentmemory/commit/1b0b458ef43e69ff4897b2c673bb01608aec8e87
1b0b458https://github.com/Fguarien/agentmemory/commit/1b0b458ef43e69ff4897b2c673bb01608aec8e87
https://github.com/Fguarien
Fguarienhttps://github.com/Fguarien
on Nov 1, 2026https://github.com/rohitg00/agentmemory/issues/1579#issuecomment-6558727966
c6c476dhttps://github.com/rohitg00/agentmemory/commit/c6c476d2905926727fa5848a134d7a3b38a7fa98
#1649https://github.com/rohitg00/agentmemory/pull/1649
Sign up for freehttps://github.com/signup?return_to=https://github.com/rohitg00/agentmemory/issues/1579
Sign in to commenthttps://github.com/login?return_to=https://github.com/rohitg00/agentmemory/issues/1579
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.