Skip to content

fix(gateway/restart): write sentinel with continuationMessage on coalesced restart#74443

Closed
hclsys wants to merge 1 commit into
openclaw:mainfrom
hclsys:fix/gateway-restart-coalesced-continuation-74424
Closed

fix(gateway/restart): write sentinel with continuationMessage on coalesced restart#74443
hclsys wants to merge 1 commit into
openclaw:mainfrom
hclsys:fix/gateway-restart-coalesced-continuation-74424

Conversation

@hclsys

@hclsys hclsys commented Apr 29, 2026

Copy link
Copy Markdown

Problem

When scheduleGatewaySigusr1Restart returns { coalesced: true } (two paths in src/infra/restart.ts), the beforeEmit hook registered by the gateway tool is never called. This means writeRestartSentinel never fires, so the continuationMessage supplied by the caller is silently dropped.

Fix

After scheduleGatewaySigusr1Restart returns, if the result is coalesced and the caller supplied a continuationMessage, call rewriteRestartSentinel to merge the continuation into any existing sentinel file. The call is fire-and-forget (.catch(() => undefined)) so a missing sentinel does not surface as an error on the coalesced path.

Testing

  • Typecheck passes: tsc --noEmit --project tsconfig.core.json
  • Two paths fixed: in-flight coalesced (no hooks called at all) and already-scheduled coalesced (updatePendingRestartEmitHooks overwrites, does not chain)

Fixes #74424.

…esced restart

When scheduleGatewaySigusr1Restart returns coalesced=true, the beforeEmit
hook is never called, so writeRestartSentinel never fires and any
continuationMessage passed by the caller is silently dropped.

Call rewriteRestartSentinel after the coalesced return to merge the
incoming continuationMessage into any existing sentinel.

Fixes openclaw#74424.
@aisle-research-bot

aisle-research-bot Bot commented Apr 29, 2026

Copy link
Copy Markdown

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟡 Medium Cross-session continuation overwrite when coalescing gateway restart requests
1. 🟡 Cross-session continuation overwrite when coalescing gateway restart requests
Property Value
Severity Medium
CWE CWE-200
Location src/agents/tools/gateway-tool.ts:425-433

Description

In gateway-tool restart handling, when a restart request is coalesced with an already-pending restart, the code rewrites the (global) restart sentinel file to add the new request's continuationMessage.

  • The restart sentinel is a single global file (restart-sentinel.json) in the state directory.
  • rewriteRestartSentinel rewrites the existing sentinel payload without validating that the existing payload belongs to the same sessionKey.
  • If two different sessions/channels request a restart close together and the second request is marked coalesced, the second request can overwrite payload.continuation for the first request.
  • After boot, server-restart-sentinel uses payload.sessionKey and stored routing context to enqueue and deliver the continuation; overwriting continuation can therefore misroute or leak continuation text across sessions/chats.

Vulnerable code:

if (scheduled.coalesced && continuationMessage) {
  await rewriteRestartSentinel((existing) => ({
    ...existing,
    continuation: buildRestartSuccessContinuation({ sessionKey, continuationMessage }),
  })).catch(() => undefined);
}

Recommendation

Ensure sentinel rewrites cannot mix data across sessions.

Minimal safe guard: only rewrite the sentinel if it matches the current request's session identity (and optionally delivery context), otherwise leave it unchanged.

if (scheduled.coalesced && continuationMessage) {
  await rewriteRestartSentinel((existing) => {
    if ((existing.sessionKey ?? "").trim() !== (sessionKey ?? "").trim()) {
      return existing; // do not overwrite another session's continuation
    }
    return {
      ...existing,
      continuation: buildRestartSuccessContinuation({ sessionKey, continuationMessage }),
    };
  });
}

More robust options:

  • Store multiple continuations keyed by sessionKey (array/map) and have post-restart logic enqueue each to its own session.
  • Alternatively, include a restart request token/id in the sentinel and only allow rewrites that present the same token.

Analyzed PR: #74443 at commit d0216a8

Last updated on: 2026-04-29T15:26:50Z

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS r: too-many-prs Auto-close: author has more than twenty active PRs. labels Apr 29, 2026
@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because the author has more than 10 active PRs in this repo. Please reduce the active PR queue and reopen or resubmit once it is back under the limit. You can close your own PRs to get back under the limit.

@greptile-apps

greptile-apps Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a bug where continuationMessage was silently dropped when a gateway restart request coalesced with an already-pending or already-in-flight restart (i.e., the beforeEmit hook registered by the coalesced caller was never invoked). The fix adds a rewriteRestartSentinel call after scheduleGatewaySigusr1Restart returns { coalesced: true } to merge the continuation into any existing sentinel file.

Confidence Score: 4/5

Safe to merge — the fix is targeted and well-scoped, with errors swallowed to avoid surfacing on the coalesced path.

No P0 or P1 issues found. The rewriteRestartSentinel call is redundant (no-op) for the already-scheduled + not yet preparing sub-path because updatePendingRestartEmitHooks already ensures the new beforeEmit will run, but it is harmless. All edge cases around missing sentinels are handled by .catch(() => undefined). The fix correctly targets path 1 (in-flight) and is benign for path 2 (scheduled).

No files require special attention.

Reviews (1): Last reviewed commit: "fix(gateway/restart): write sentinel wit..." | Re-trigger Greptile

openperf added a commit to openperf/moltbot that referenced this pull request May 27, 2026
…t continuations (openclaw#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt openclaw#74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching openclaw#83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs openclaw#86742
openperf added a commit to openperf/moltbot that referenced this pull request May 27, 2026
…t continuations (openclaw#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt openclaw#74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching openclaw#83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs openclaw#86742
openperf added a commit to openperf/moltbot that referenced this pull request May 27, 2026
…t continuations (openclaw#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt openclaw#74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching openclaw#83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs openclaw#86742
openperf added a commit to openperf/moltbot that referenced this pull request May 27, 2026
…t continuations (openclaw#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt openclaw#74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching openclaw#83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs openclaw#86742
steipete pushed a commit to openperf/moltbot that referenced this pull request Jun 7, 2026
…t continuations (openclaw#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt openclaw#74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching openclaw#83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs openclaw#86742
steipete pushed a commit to openperf/moltbot that referenced this pull request Jun 7, 2026
…t continuations (openclaw#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt openclaw#74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching openclaw#83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs openclaw#86742
steipete pushed a commit to openperf/moltbot that referenced this pull request Jun 7, 2026
…t continuations (openclaw#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt openclaw#74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching openclaw#83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs openclaw#86742
steipete pushed a commit to openperf/moltbot that referenced this pull request Jun 7, 2026
…t continuations (openclaw#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt openclaw#74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching openclaw#83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs openclaw#86742
steipete pushed a commit to openperf/moltbot that referenced this pull request Jun 7, 2026
…t continuations (openclaw#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt openclaw#74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching openclaw#83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs openclaw#86742
steipete added a commit that referenced this pull request Jun 7, 2026
…t continuations (#86742) (#87323)

* fix(infra/agents): session-routing guard for coalesced gateway restart continuations (#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt #74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching #83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs #86742

* fix(infra): preserve queued restart continuation on forced bypass

* fix(infra): make forced restart hook preservation explicit

* fix(infra): guard restart continuation ownership before reschedule

* fix(infra): report hookless coalesced restarts accurately

* fix(infra): trust runtime session for restart sentinel routing

* fix(infra): preserve earlier restart reschedule semantics

* fix(agents): trust runtime session for update continuations

* fix(infra): preserve hookless forced restart continuations

---------

Co-authored-by: Peter Steinberger <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 8, 2026
…t continuations (openclaw#86742) (openclaw#87323)

* fix(infra/agents): session-routing guard for coalesced gateway restart continuations (openclaw#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt openclaw#74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching openclaw#83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs openclaw#86742

* fix(infra): preserve queued restart continuation on forced bypass

* fix(infra): make forced restart hook preservation explicit

* fix(infra): guard restart continuation ownership before reschedule

* fix(infra): report hookless coalesced restarts accurately

* fix(infra): trust runtime session for restart sentinel routing

* fix(infra): preserve earlier restart reschedule semantics

* fix(agents): trust runtime session for update continuations

* fix(infra): preserve hookless forced restart continuations

---------

Co-authored-by: Peter Steinberger <[email protected]>
wangmiao0668000666 pushed a commit to wangmiao0668000666/openclaw that referenced this pull request Jun 9, 2026
…t continuations (openclaw#86742) (openclaw#87323)

* fix(infra/agents): session-routing guard for coalesced gateway restart continuations (openclaw#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt openclaw#74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching openclaw#83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs openclaw#86742

* fix(infra): preserve queued restart continuation on forced bypass

* fix(infra): make forced restart hook preservation explicit

* fix(infra): guard restart continuation ownership before reschedule

* fix(infra): report hookless coalesced restarts accurately

* fix(infra): trust runtime session for restart sentinel routing

* fix(infra): preserve earlier restart reschedule semantics

* fix(agents): trust runtime session for update continuations

* fix(infra): preserve hookless forced restart continuations

---------

Co-authored-by: Peter Steinberger <[email protected]>
badgerbees pushed a commit to badgerbees/openclaw that referenced this pull request Jul 8, 2026
…t continuations (openclaw#86742) (openclaw#87323)

* fix(infra/agents): session-routing guard for coalesced gateway restart continuations (openclaw#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt openclaw#74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching openclaw#83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs openclaw#86742

* fix(infra): preserve queued restart continuation on forced bypass

* fix(infra): make forced restart hook preservation explicit

* fix(infra): guard restart continuation ownership before reschedule

* fix(infra): report hookless coalesced restarts accurately

* fix(infra): trust runtime session for restart sentinel routing

* fix(infra): preserve earlier restart reschedule semantics

* fix(agents): trust runtime session for update continuations

* fix(infra): preserve hookless forced restart continuations

---------

Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling r: too-many-prs Auto-close: author has more than twenty active PRs. size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: gateway restart continuationMessage is silently dropped when restart coalesces with an in-flight restart

1 participant