Skip to content

memory-core dreaming cleanup fails: subagent deleteSession cannot dispatch sessions.delete (operator.admin required) #72712

@BSG2000

Description

@BSG2000

Symptom

When the memory-core dreaming pipeline finishes a narrative cycle (cron-driven
or manual), session cleanup fails with:

[memory-core/dreaming] failed to delete dreaming subagent session: missing scope: operator.admin

This recurs on every dreaming run. The narrative completes (memories are
written) but the ephemeral subagent session is never reaped, leaving stale
session records.

Environment

  • openclaw 2026.4.24 (also reproduced against current main, commit 0286bb98)
  • Linux / WSL Ubuntu-24.04, Node 22
  • Plugins enabled: active-memory, browser, memory-core, memory-wiki, whatsapp
  • Cron-driven dreaming (memory.dreaming.* jobs registered via cron)
  • Reproduces with default cron.enabled: true

Trace

The dreaming worker calls subagent.deleteSession(...) on the plugin runtime.
That hits createGatewaySubagentRuntime().deleteSession in
src/gateway/server-plugins.ts (≈ line 380):

async deleteSession(params) {
  await dispatchGatewayMethod("sessions.delete", {
    key: params.sessionKey,
    deleteTranscript: params.deleteTranscript ?? true,
  });
},

dispatchGatewayMethod looks up a request scope via
getPluginRuntimeGatewayRequestScope(). When the dreaming job is invoked from
the cron service / fallback gateway context (not a live operator request), no
scope is set, so dispatchGatewayMethod falls back to
createSyntheticOperatorClient() which mints a client with
scopes: [WRITE_SCOPE].

But sessions.delete is in the ADMIN_SCOPE list
(src/gateway/method-scopes.ts ≈ line 175), so authorization fails with
missing scope: operator.admin.

The existing test asserts this is the intentional behavior:

src/gateway/server-plugins.test.ts
test("rejects fallback session deletion without minting admin scope", …)
…rejects.toThrow("missing scope: operator.admin")

So the gateway side is by design. The contract is: a caller wanting to delete
a session must already be in a withPluginRuntimeGatewayRequestScope whose
client carries operator.admin (the second test, "allows session deletion when
the request scope already has admin", confirms this works).

Where the contract is broken

The memory-core dreaming worker (bundled plugin) fires its cleanup from a
cron callback. It does not appear to wrap the cleanup in
withPluginRuntimeGatewayRequestScope with an admin client, so it inevitably
hits the synthetic operator.write fallback and is rejected.

Possible fixes (need maintainer guidance — not opening a PR yet)

  1. Memory-core side: wrap dreaming cleanups in
    withPluginRuntimeGatewayRequestScope with an admin-scoped synthetic
    client. Memory-core is a trusted bundled plugin, so this is reasonable —
    but the wrapping helper isn't currently exposed to plugin code.

  2. Plugin runtime side: introduce an opt-in
    subagent.deleteSession({ asAdmin: true }) (or a new
    subagent.adminDeleteSession) which is gated by an explicit per-plugin
    policy (similar to the existing allowModelOverride allowlist in
    setPluginSubagentOverridePolicies). This keeps the synthetic-fallback
    safety net intact while giving trusted plugins a documented escape hatch.

  3. Method-scopes side: split the lifecycle of operator-initiated
    session deletes vs. plugin-initiated, plugin-owned session deletes into
    two methods, with the plugin variant requiring only operator.write plus
    ownership proof.

Workaround

For self-hosted users hitting this on every dreaming run, a local patch to the
distributed dist/server-plugin-bootstrap-*.js to pass
{ syntheticScopes: ["operator.admin"] } from deleteSession will silence
the error — but this is not a correct upstream change because it removes the
security boundary asserted by the test above.

Reproduce

  1. Enable memory-core and set cron.enabled: true.
  2. Trigger a dreaming run (openclaw cron run memory.dreaming.narrative or
    wait for the schedule).
  3. Observe journalctl --user -u openclaw-gateway:
    [memory-core/dreaming] failed to delete dreaming subagent session: missing scope: operator.admin

Disclosure: Drafted with assistance from GitHub Copilot CLI based on
diagnostics from a self-hosted openclaw deployment.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions