Skip to content

fix: inject pluginConfig into hook handler event context#72888

Merged
steipete merged 3 commits into
openclaw:mainfrom
jalapeno777:fix/pluginconfig-hook-injection
Apr 27, 2026
Merged

fix: inject pluginConfig into hook handler event context#72888
steipete merged 3 commits into
openclaw:mainfrom
jalapeno777:fix/pluginconfig-hook-injection

Conversation

@jalapeno777

Copy link
Copy Markdown

Problem

When plugins register hooks via api.registerHook(), the pluginConfig from openclaw.json is not injected into the hook event context. Any plugin that accesses event.context.pluginConfig inside a hook handler receives undefined.

Root Cause

In src/plugins/registry.ts:

  • registerHook() (line 419) passes the handler directly to registerInternalHook(event, handler) without wrapping — no pluginConfig is added to the event context
  • createApi() (line 1469) passes params.config but not params.pluginConfig to registerHook

Impact

  • Plugins that access ctx.pluginConfig in hook handlers silently get undefined
  • Plugins that fall back to defaults (ctx.pluginConfig ?? {}) ignore user configuration
  • api.pluginConfig is available at registration time but NOT at hook invocation time

Fix

  1. Add pluginConfig parameter to registerHook()
  2. Wrap the handler to inject pluginConfig into event.context before each invocation
  3. Pass params.pluginConfig through from createApi()

Testing

Verified locally on v2026.4.22 with a custom plugin (ava-bootstrap-inject) that reads pluginConfig.files from the hook context. Before patch: undefined → plugin was a silent no-op. After patch: config resolves correctly and files are injected.

Debug log confirming fix:

[ava-bootstrap-inject] hook fired. sessionKey=agent:main:main 
pluginConfig={"files":["memory/alive.md","memory/daily_focus.md","memory/session_primer.md"]} 
filesResolved=["memory/alive.md","memory/daily_focus.md","memory/session_primer.md"]

Fixes #72880

@greptile-apps

greptile-apps Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a bug where pluginConfig from openclaw.json was not available inside hook handlers by wrapping each registered handler to inject the config into event.context. The plumbing through createApiregisterHook is correct, but the wrapper mutates the shared event object directly instead of cloning it, which can contaminate other handlers in the same dispatch chain.

  • P1: wrappedHandler writes directly to evt.context, which is the same object triggerInternalHook passes to every handler in sequence — non-plugin handlers and other plugins' handlers that fire after the first mutation will see an unexpected pluginConfig key. A shallow spread ({ ...evt, context: { ...evt.context, pluginConfig } }) avoids this entirely.

Confidence Score: 3/5

Hold — the wrapper mutates the shared event context in-place, contaminating other handlers in the same dispatch loop.

One P1 defect: the wrappedHandler mutates the shared event object instead of creating a shallow copy, which causes cross-handler context contamination at runtime. The overall plumbing change is correct and the fix is small, so this does not rise to P0, but the mutation bug needs to be addressed before merging.

src/plugins/registry.ts — specifically the wrappedHandler block around lines 505–510.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/plugins/registry.ts
Line: 505-510

Comment:
**Shared event object mutated in-place**

`triggerInternalHook` passes the **same** `event` reference to every registered handler sequentially (see `internal-hooks.ts` lines 296–305). Mutating `evt.context` here permanently writes `pluginConfig` onto the live event object before any subsequent handler (including non-plugin internal handlers and other plugins' wrapped handlers) receives it. If Plugin A and Plugin B both hook the same event, Plugin A's value is silently overwritten, and all non-plugin handlers that run after either plugin will see an unexpected `pluginConfig` key that does not belong to their context type.

Shallow-copy the event and its context instead of mutating the incoming object:

```suggestion
      const wrappedHandler: typeof handler = async (evt) => {
        return handler({
          ...evt,
          context: { ...evt.context, pluginConfig },
        });
      };
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix: inject pluginConfig into hook handl..." | Re-trigger Greptile

Comment thread src/plugins/registry.ts
Comment on lines +505 to +510
const wrappedHandler: typeof handler = async (evt) => {
if (evt.context && typeof evt.context === "object") {
(evt.context as Record<string, unknown>).pluginConfig = pluginConfig;
}
return handler(evt);
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Shared event object mutated in-place

triggerInternalHook passes the same event reference to every registered handler sequentially (see internal-hooks.ts lines 296–305). Mutating evt.context here permanently writes pluginConfig onto the live event object before any subsequent handler (including non-plugin internal handlers and other plugins' wrapped handlers) receives it. If Plugin A and Plugin B both hook the same event, Plugin A's value is silently overwritten, and all non-plugin handlers that run after either plugin will see an unexpected pluginConfig key that does not belong to their context type.

Shallow-copy the event and its context instead of mutating the incoming object:

Suggested change
const wrappedHandler: typeof handler = async (evt) => {
if (evt.context && typeof evt.context === "object") {
(evt.context as Record<string, unknown>).pluginConfig = pluginConfig;
}
return handler(evt);
};
const wrappedHandler: typeof handler = async (evt) => {
return handler({
...evt,
context: { ...evt.context, pluginConfig },
});
};
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/plugins/registry.ts
Line: 505-510

Comment:
**Shared event object mutated in-place**

`triggerInternalHook` passes the **same** `event` reference to every registered handler sequentially (see `internal-hooks.ts` lines 296–305). Mutating `evt.context` here permanently writes `pluginConfig` onto the live event object before any subsequent handler (including non-plugin internal handlers and other plugins' wrapped handlers) receives it. If Plugin A and Plugin B both hook the same event, Plugin A's value is silently overwritten, and all non-plugin handlers that run after either plugin will see an unexpected `pluginConfig` key that does not belong to their context type.

Shallow-copy the event and its context instead of mutating the incoming object:

```suggestion
      const wrappedHandler: typeof handler = async (evt) => {
        return handler({
          ...evt,
          context: { ...evt.context, pluginConfig },
        });
      };
```

How can I resolve this? If you propose a fix, please make it concise.

@jalapeno777

Copy link
Copy Markdown
Author

CI Failure Analysis

Both failing checks are unrelated to this PR:

  1. checks-node-agentic-control-plane — Failed with GatewayClientRequestError: device metadata change pending approval. This is a CI runner infrastructure issue (device pairing/approval). The test shard timed out (exit 143) after 422s. Not related to the registry.ts change.

  2. checks-node-core — Aggregation gate that failed because the non-dist shard above failed. DIST_SHARD_RESULT: success.

All plugin-adjacent checks passed:

  • checks-fast-contracts-plugins
  • checks-node-agentic-plugins
  • checks-node-agentic-plugin-sdk
  • check-prod-types
  • check-test-types
  • check-lint

@steipete steipete force-pushed the fix/pluginconfig-hook-injection branch from 13ceff7 to aa81327 Compare April 27, 2026 19:33
@steipete steipete force-pushed the fix/pluginconfig-hook-injection branch 3 times, most recently from d7b2a73 to a574c70 Compare April 27, 2026 20:10
Ayumi Server and others added 3 commits April 27, 2026 21:12
When plugins register hooks via api.registerHook(), pluginConfig from
openclaw.json was not available in the hook event context. Plugins that
accessed ctx.pluginConfig or event.context.pluginConfig received
undefined, causing silent failures or fallback to defaults.

Changes:
- Add pluginConfig parameter to registerHook() function
- Wrap handler to inject pluginConfig into event.context before invocation
- Pass params.pluginConfig through createApi() call site

Fixes #72880
Address review feedback on PR #72888. triggerInternalHook passes the
same event reference to all handlers sequentially. Mutating evt.context
leaks pluginConfig to subsequent handlers and causes cross-plugin
overwrites. Shallow-copy event and context instead.
@steipete steipete force-pushed the fix/pluginconfig-hook-injection branch from a574c70 to 0980f7a Compare April 27, 2026 20:12
@steipete steipete merged commit be2196c into openclaw:main Apr 27, 2026
65 checks passed
steipete pushed a commit that referenced this pull request Apr 27, 2026
Address review feedback on PR #72888. triggerInternalHook passes the
same event reference to all handlers sequentially. Mutating evt.context
leaks pluginConfig to subsequent handlers and causes cross-plugin
overwrites. Shallow-copy event and context instead.
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
Address review feedback on PR openclaw#72888. triggerInternalHook passes the
same event reference to all handlers sequentially. Mutating evt.context
leaks pluginConfig to subsequent handlers and causes cross-plugin
overwrites. Shallow-copy event and context instead.
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
Address review feedback on PR openclaw#72888. triggerInternalHook passes the
same event reference to all handlers sequentially. Mutating evt.context
leaks pluginConfig to subsequent handlers and causes cross-plugin
overwrites. Shallow-copy event and context instead.
globalcaos pushed a commit to globalcaos/tinkerclaw that referenced this pull request May 13, 2026
Address review feedback on PR openclaw#72888. triggerInternalHook passes the
same event reference to all handlers sequentially. Mutating evt.context
leaks pluginConfig to subsequent handlers and causes cross-plugin
overwrites. Shallow-copy event and context instead.
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
Address review feedback on PR openclaw#72888. triggerInternalHook passes the
same event reference to all handlers sequentially. Mutating evt.context
leaks pluginConfig to subsequent handlers and causes cross-plugin
overwrites. Shallow-copy event and context instead.
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
Address review feedback on PR openclaw#72888. triggerInternalHook passes the
same event reference to all handlers sequentially. Mutating evt.context
leaks pluginConfig to subsequent handlers and causes cross-plugin
overwrites. Shallow-copy event and context instead.
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
Address review feedback on PR openclaw#72888. triggerInternalHook passes the
same event reference to all handlers sequentially. Mutating evt.context
leaks pluginConfig to subsequent handlers and causes cross-plugin
overwrites. Shallow-copy event and context instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: pluginConfig not injected into hook handler event context for api.registerHook

2 participants