Skip to content

fix(tools): bound memory forget requests - #1451

Merged
ishaanxgupta merged 1 commit into
supermemoryai:mainfrom
abhinavkr26104:fix/memory-forget-timeout
Aug 13, 2026
Merged

ishaanxgupta merged 1 commit into
supermemoryai:mainfrom
abhinavkr26104:fix/memory-forget-timeout

Conversation

@abhinavkr26104

@abhinavkr26104 abhinavkr26104 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add a 30-second default timeout to the shared DELETE /v4/memories helper.
  • Allow callers to provide an AbortSignal for cancellation.
  • Add regression coverage for both the default timeout signal and caller-provided signals.

This prevents AI SDK and OpenAI tool memory-forget calls from remaining pending indefinitely when the API or network stalls.

Fixes #1452

Testing

  • bunx vitest run packages/tools/src/tool-operations.test.ts — 14 passed
  • bun run --cwd packages/tools build — passed
  • bunx biome check packages/tools/src/shared/forget-memory.ts packages/tools/src/tool-operations.test.ts — passed

The full tools test command still has two pre-existing failures requiring SUPERMEMORY_API_KEY and a missing test/claude-memory module. Package type-checking also reports existing dependency/test errors unrelated to this change.

Checklist

  • Minimal targeted change
  • Tests added for default timeout and explicit cancellation
  • No unrelated files included

@ishaanxgupta
ishaanxgupta merged commit 82dae50 into supermemoryai:main Aug 13, 2026
4 checks passed
addyCooks added a commit to addyCooks/supermemory that referenced this pull request Aug 19, 2026
supermemoryai#1451 bounded `DELETE /v4/memories` with a 30s abort, but the two signals
were selected between with `??`:

    signal: options?.signal ?? AbortSignal.timeout(FETCH_TIMEOUT_MS)

so they were mutually exclusive. Any caller who passed a cancellation
signal silently dropped the timeout and the request went unbounded
again - exactly the hang supermemoryai#1451 set out to remove - and there was no way
to ask for both through the API. No production call site passes options
today (ai-sdk.ts and openai/tools.ts both omit it), so this was latent.

Composes the two with `AbortSignal.any` instead, so a caller signal
cancels the request and the 30s ceiling still applies. The timeout
signal is built once and reused for the bare case.

Verified against a server that never responds: before this change the
caller-signal path hangs indefinitely; after it, it rejects with
TimeoutError at the deadline, an early caller abort still wins with
AbortError, and an already-aborted signal rejects immediately.

Test coverage in tool-operations.test.ts replaces the case that asserted
the old select-one behaviour with four: caller abort propagates to the
composed signal, the timeout still fires while the caller signal stays
live, an already-aborted caller signal is forwarded, and an options
object without a signal still gets the timeout.
addyCooks added a commit to addyCooks/supermemory that referenced this pull request Aug 19, 2026
supermemoryai#1451 bounded `DELETE /v4/memories` with a 30s abort, but the two signals
were selected between with `??`:

    signal: options?.signal ?? AbortSignal.timeout(FETCH_TIMEOUT_MS)

so they were mutually exclusive. Any caller who passed a cancellation
signal silently dropped the timeout and the request went unbounded
again - exactly the hang supermemoryai#1451 set out to remove - and there was no way
to ask for both through the API. No production call site passes options
today (ai-sdk.ts and openai/tools.ts both omit it), so this was latent.

Composes the two with `AbortSignal.any` instead, so a caller signal
cancels the request and the 30s ceiling still applies. The timeout
signal is built once and reused for the bare case.

Verified against a server that never responds: before this change the
caller-signal path hangs indefinitely; after it, it rejects with
TimeoutError at the deadline, an early caller abort still wins with
AbortError, and an already-aborted signal rejects immediately.

tool-operations.test.ts replaces the case that asserted the old
select-one behaviour with five: the caller signal is composed rather
than substituted and its abort reason reaches the request, the timeout
still fires while the caller signal stays open, an already-aborted
caller signal is forwarded, the bare path still gets the 30s timeout
signal itself, and an aborted fetch surfaces to the caller.

Each case was checked against mutants of the fix - dropping the timeout
from AbortSignal.any, composing with a signal that never fires, reverting
to ??, and shortening the timeout - and every mutant fails at least one
of them.
Agnik47 added a commit to Agnik47/supermemory that referenced this pull request Aug 25, 2026
`forgetMemoryRequest` combined the caller's signal and the 30s abort with
`??`, making them mutually exclusive. Passing a cancellation signal removed
the timeout, so a hung `DELETE /v4/memories` could wedge the tool call again
— the exact condition supermemoryai#1451 set out to remove. There was also no way for a
caller to ask for both cancellation and a timeout.

Compose the two with `AbortSignal.any` instead of choosing between them.
`AbortSignal.any` is available in Node 20.3+, Bun and workerd.

No production call site passes `options` today (`ai-sdk.ts` and
`openai/tools.ts` both omit it), so this was latent rather than live.

The existing test asserted the buggy behaviour (`init.signal` being the
caller's own signal), so it is replaced by two tests that pin the composed
semantics: aborting the caller aborts the request, and the timeout leg still
aborts the request on its own. Both fail against the previous implementation.

Fixes supermemoryai#1549
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.

fix(tools): prevent memory-forget requests from hanging indefinitely

2 participants