Skip to content

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

@jalapeno777

Description

@jalapeno777

Summary

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

Affected Code

File: src/plugins/registry.ts

registerHook function (line 419): The handler is passed directly to registerInternalHook(event, handler) at line 502 without wrapping. The hook event context is built by createInternalHookEvent() which only sets workspaceDir, bootstrapFiles, cfg, sessionKey, sessionId, agentId — no pluginConfig.

createApi call site (line 1469): Passes params.config but not params.pluginConfig to registerHook.

Impact

  • Any plugin using api.registerHook() that needs to read its configured pluginConfig from the hook context will silently get undefined
  • api.pluginConfig is available at registration time but NOT at hook invocation time
  • Plugins that fall back to defaults still work but ignore user configuration
  • Plugins that guard on ctx.pluginConfig being defined silently no-op

Reproduction

  1. Create a plugin with a config schema and some config in openclaw.json
  2. Register a hook: api.registerHook("agent:bootstrap", async (event) => { const cfg = event.context.pluginConfig; /* undefined */ })
  3. Observe that pluginConfig is always undefined

Suggested Fix

Pass pluginConfig to registerHook and wrap the handler to inject it:

// registry.ts line 419 — add pluginConfig parameter
const registerHook = (
  record: PluginRecord,
  events: string | string[],
  handler: Parameters<typeof registerInternalHook>[1],
  opts: OpenClawPluginHookOptions | undefined,
  config: OpenClawPluginApi["config"],
  pluginConfig: unknown,  // <-- add this
) => {
  // ... existing code ...
  
  // line 502 — wrap handler to inject pluginConfig
  for (const event of normalizedEvents) {
    const wrappedHandler = async (evt: InternalHookEvent) => {
      if (evt.context && typeof evt.context === "object") {
        evt.context.pluginConfig = pluginConfig;
      }
      return handler(evt);
    };
    registerInternalHook(event, wrappedHandler);
    nextRegistrations.push({ event, handler: wrappedHandler });
  }
// line 1469 — pass pluginConfig
registerHook(record, events, handler, opts, params.config, params.pluginConfig),

Workaround

Plugins can capture api.pluginConfig at registration time via closure:

register(api) {
  const config = api.pluginConfig;
  api.registerHook("agent:bootstrap", async (event) => {
    const cfg = event.context.pluginConfig ?? config; // fallback to closure
  });
}

Versions

  • Reproduced on: 2026.4.22, 2026.4.25
  • Confirmed on main branch (registry.ts)

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