Skip to content

Channel config-schema additions in downstream patches not picked up by runtime validator (codegen-bundled JSON schema is checked-in, not regenerated at build) #80536

Description

@markthebest12

TL;DR

When a downstream patch adds a field to an extension's extensions/<channel>/src/config-schema.ts zod schema, the field is correctly compiled into dist/extensions/<channel>/config-schema-*.js but is NOT picked up by the runtime config validator. The validator gates channels.<channel>.<key> against src/config/bundled-channel-config-metadata.generated.ts — a checked-in codegen output that is not regenerated as part of npm run build. Without an entry in that file, the gateway rejects the new field at startup with:

channels.<channel>: invalid config: must NOT have additional properties
Run "openclaw doctor --fix" to repair, then retry.
Gateway failed to start: Error: Invalid config at /Users/<u>/.openclaw/openclaw.json

Repro (minimal)

  1. Patch extensions/bluebubbles/src/config-schema.ts to add an optional zod field, e.g.:
    foo: z.string().optional(),
  2. Run npm run build (full pnpm install && npm run build chain).
  3. Confirm the field appears in dist/extensions/bluebubbles/config-schema-*.js (it does — the per-extension bundle compiles correctly).
  4. Confirm the field does NOT appear in src/config/bundled-channel-config-metadata.generated.ts (it doesn't — the codegen wasn't re-run).
  5. Add channels.bluebubbles.foo: "bar" to ~/.openclaw/openclaw.json.
  6. Start the gateway. It crashes with channels.bluebubbles: invalid config: must NOT have additional properties.

Why it happens

src/config/bundled-channel-config-metadata.generated.ts carries the .generated.ts suffix and is bundled into dist/io-*.js (and consumed by src/channels/plugins/config-schema.ts via safeParseJsonSchema(schema, options?.cacheKey ?? "channel-config-schema:json", value)). The file IS marked generated, but it's checked into source control and not re-run from the per-extension zod schemas during npm run build. So zod source changes that don't include a hand-edit (or codegen run) of this bundled file silently ship a half-broken state: per-extension bundle has the field, runtime validator does not.

Suggested fix (one of)

A. Best: hook the codegen step into npm run build (e.g., as a prebuild script) so the bundled file regenerates from the per-extension zod sources on every build. Removes the trap entirely for downstream patches and refactors.

B. Cheapest: add an npm run build post-step that asserts every property in the per-extension config-schema.ts zod schemas is present in the bundled JSON schema for that channel. Fails the build with a clear message pointing at the missing keys.

C. Workaround for downstream patches (current state): every patch that touches extensions/<channel>/src/config-schema.ts must mirror the additions in src/config/bundled-channel-config-metadata.generated.ts at both the channel-level and the per-account level. Brittle and easy to miss — we hit this exactly in our downstream and reproduced the diagnostic chain documented in https://github.com/markthebest12/openclaw-infra/issues/1258 (filed today after our gateways crashlooped on activation of a new BlueBubbles peerAgents field).

Real-world impact

We added three optional fields (peerAgents: string[], peerAgentsRequireMention: boolean, mentionName: string) to extensions/bluebubbles/src/config-schema.ts via a downstream nix-flake overlay (openclaw-infra#1258). All upstream-visible CI/build steps passed. Activation in production crashlooped both of our gateways with the additional-properties error. We had to ship a wrapper patch (https://github.com/markthebest12/openclaw-infra/pull/1464) that mirrors the additions into src/config/bundled-channel-config-metadata.generated.ts at both the channel-level (schema.properties) and the per-account level (schema.properties.accounts.items.properties).

The bb-inbound-humanizing.patch upstream-equivalent in our overlay already does this correctly (it modifies the bundled file when adding debugWebhookPayloads). Without that prior pattern as a reference, we'd have spent much longer diagnosing the asymmetry between zod source and the runtime validator.

Environment

  • openclaw v2026.5.6 (per nix-openclaw rev 44ae6995de50)
  • Node.js v22.22.2
  • macOS 26.4.1
  • Per-user gateway config at /Users/<user>/.openclaw/openclaw.json

Proposed acceptance for B (build-time guard)

// In a build-time check (or a generator script)
import { ChannelConfigSchemaRegistry } from "./extensions/.../config-schema";
import { BUNDLED_CHANNEL_CONFIG_METADATA } from "./src/config/bundled-channel-config-metadata.generated";

for (const channel of BUNDLED_CHANNEL_CONFIG_METADATA) {
  const zodKeys = new Set(Object.keys(getZodObjectShape(ChannelConfigSchemaRegistry[channel.pluginId])));
  const jsonKeys = new Set(Object.keys(channel.schema.properties));
  const missing = [...zodKeys].filter(k => !jsonKeys.has(k));
  if (missing.length > 0) {
    throw new Error(`bundled-channel-config-metadata.generated.ts missing keys for ${channel.pluginId}: ${missing.join(', ')} — re-run codegen`);
  }
}

Happy to send a PR if helpful — let us know which approach you'd prefer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.clownfishTracked by Clownfish automationimpact:crash-loopCrash, hang, restart loop, or process-level availability failure.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions