openclaw - 💡(How to fix) Fix Plugin hook: before_message_write not fired when before_dispatch returns handled:true — no way to suppress inbound message from session transcript [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#62061Fetched 2026-04-08 03:09:33
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Participants
Timeline (top)
labeled ×1

When a plugin's before_dispatch hook returns { handled: true }, the inbound user message is already persisted to the session transcript with no hook available to suppress it, making it impossible for a plugin to both handle a message and prevent it from appearing in future LLM context.

Root Cause

When a plugin's before_dispatch hook returns { handled: true }, the inbound user message is already persisted to the session transcript with no hook available to suppress it, making it impossible for a plugin to both handle a message and prevent it from appearing in future LLM context.

Fix Action

Fix / Workaround

When a plugin's before_dispatch hook returns { handled: true }, the inbound user message is already persisted to the session transcript with no hook available to suppress it, making it impossible for a plugin to both handle a message and prevent it from appearing in future LLM context.

  1. Register a plugin with both before_dispatch and before_message_write hooks
  2. In before_dispatch, return { handled: true } for a specific message pattern Winston Dev02  [11:10 AM]
  3. Send a matching message through any channel
  4. Inspect the session transcript file after the turn completes
  5. Observe: the inbound user message is present in the transcript despite being handled and never forwarded to the LLM

Expected behavior: A plugin returning { handled: true } from before_dispatch should have a mechanism to also prevent the inbound message from being written to the session transcript — either via a suppressTranscript flag on the before_dispatch return value, by firing before_message_write with enough context for the plugin to conditionally block it, or via a dedicated post-handle hook that can tombstone the written entry.

Code Example

Dispatch early-return path — dist/dispatch-C2z_6KF9.js ~430457:
Winston Dev02  [11:10 AM]
if (hookRunner?.hasHooks("before_dispatch")) {
  const beforeDispatchResult = await hookRunner.runBeforeDispatch({ ... });
  if (beforeDispatchResult?.handled) {
    recordProcessed("completed", { reason: "before_dispatch_handled" });
    return { ... };  // ← returns AFTER transcript write already occurred
  }
}
Transcript write path — dist/pi-embedded-BYdcxQ5A.js line ~27135 (guardedAppend) and line ~27191 (before_message_write hook wiring inside guardSessionManager). The write occurs during session setup before dispatch hook evaluation.

before_message_write is only wired inside installSessionToolResultGuard via guardSessionManager — it fires at write time with no awareness of subsequent before_dispatch disposition.
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

When a plugin's before_dispatch hook returns { handled: true }, the inbound user message is already persisted to the session transcript with no hook available to suppress it, making it impossible for a plugin to both handle a message and prevent it from appearing in future LLM context.

Steps to reproduce

  1. Register a plugin with both before_dispatch and before_message_write hooks
  2. In before_dispatch, return { handled: true } for a specific message pattern Winston Dev02  [11:10 AM]
  3. Send a matching message through any channel
  4. Inspect the session transcript file after the turn completes
  5. Observe: the inbound user message is present in the transcript despite being handled and never forwarded to the LLM

Expected behavior

Expected behavior: A plugin returning { handled: true } from before_dispatch should have a mechanism to also prevent the inbound message from being written to the session transcript — either via a suppressTranscript flag on the before_dispatch return value, by firing before_message_write with enough context for the plugin to conditionally block it, or via a dedicated post-handle hook that can tombstone the written entry.

Actual behavior

The inbound user message is written to the session transcript by installSessionToolResultGuard / guardedAppend (in Winston Dev02  [11:10 AM] pi-embedded-BYdcxQ5A.js) before the before_dispatch check runs in dispatch-C2z_6KF9.js (lines ~430–457). before_message_write fires at write time but has no way to know whether before_dispatch will subsequently handle the message. The message persists in session history and is visible to the LLM on all future turns.

OpenClaw version

v2026.4.2

Operating system

Linux 6.17.0-1009-aws x64

Install method

NOT_ENOUGH_INFO

Model

proxy/claude-sonnet-4-6

Provider / routing chain

Provider / routing chain: proxy → claude-sonnet-4-6 (Anthropic)

Additional provider/model setup details

No response

Logs, screenshots, and evidence

Dispatch early-return path — dist/dispatch-C2z_6KF9.js ~430–457:
Winston Dev02  [11:10 AM]
if (hookRunner?.hasHooks("before_dispatch")) {
  const beforeDispatchResult = await hookRunner.runBeforeDispatch({ ... });
  if (beforeDispatchResult?.handled) {
    recordProcessed("completed", { reason: "before_dispatch_handled" });
    return { ... };  // ← returns AFTER transcript write already occurred
  }
}
Transcript write path — dist/pi-embedded-BYdcxQ5A.js line ~27135 (guardedAppend) and line ~27191 (before_message_write hook wiring inside guardSessionManager). The write occurs during session setup before dispatch hook evaluation.

before_message_write is only wired inside installSessionToolResultGuard via guardSessionManager — it fires at write time with no awareness of subsequent before_dispatch disposition.

Impact and severity

Impact and severity:

• Affected: All plugins that Winston Dev02  [11:10 AM] use before_dispatch to intercept, filter, or gate messages (security filters, content moderation, command interceptors, rate limiters)

• Severity: Blocks workflow — plugins cannot fully suppress handled messages; filtered content leaks into LLM context on subsequent turns • Frequency: Always — deterministic given the execution order • Consequence: A plugin that handles and suppresses a message (e.g. a blocked command or sensitive content) cannot prevent that content from influencing the LLM's future responses. This is a correctness gap in the plugin security model and a blocker for any plugin that needs guaranteed transcript isolation.

Additional information

Secondary finding (separate issue): the ACP/agent-command transcript path (persistAcpTurnTranscript in dist/agent-command-BwUGaHZD.js line ~213) also calls

appendMessage on a raw unguarded SessionManager, meaning before_message_write is never fired at all on that path regardless of before_dispatch disposition.

extent analysis

TL;DR

To fix the issue, consider introducing a mechanism for plugins to suppress messages from being written to the session transcript, such as adding a suppressTranscript flag to the before_dispatch return value.

Guidance

  • Review the execution order of before_dispatch and transcript write operations to understand why the message is being written before the before_dispatch check runs.
  • Consider modifying the before_dispatch hook to include a suppressTranscript flag, allowing plugins to prevent handled messages from being written to the transcript.
  • Investigate the possibility of firing before_message_write with enough context for plugins to conditionally block the message write operation.
  • Evaluate the impact of introducing a dedicated post-handle hook that can tombstone written entries to prevent them from influencing future LLM responses.

Example

// Example of modified before_dispatch return value with suppressTranscript flag
const beforeDispatchResult = {
  handled: true,
  suppressTranscript: true
};

Notes

The proposed solution requires careful consideration of the plugin security model and the potential consequences of introducing new hooks or flags. The execution order of before_dispatch and transcript write operations must be carefully evaluated to ensure that the fix does not introduce new issues.

Recommendation

Apply a workaround by introducing a suppressTranscript flag to the before_dispatch return value, allowing plugins to prevent handled messages from being written to the transcript. This approach provides a targeted solution to the issue while minimizing potential disruptions to the existing plugin ecosystem.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Expected behavior: A plugin returning { handled: true } from before_dispatch should have a mechanism to also prevent the inbound message from being written to the session transcript — either via a suppressTranscript flag on the before_dispatch return value, by firing before_message_write with enough context for the plugin to conditionally block it, or via a dedicated post-handle hook that can tombstone the written entry.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING