Skip to content

CLI crashes on jiti lazy getter access for bundled plugin contracts (4.7+) #63080

Description

@tob-kai

Bug

Since 2026.4.7, all CLI commands crash with:

TypeError: Cannot read properties of undefined (reading 't')
    at Object.get [as legacyConfigRules] (.../extensions/slack/contract-api.js:1:647)
    at resolvePluginDoctorContracts (.../config-DMMR1XE_.js:220:52)

This breaks openclaw cron list, openclaw cron add, openclaw message send, openclaw health, and all other CLI operations. The gateway process is unaffected (it uses native ESM imports).

Reproduced on 2026.4.7 and 2026.4.8. Not present in 2026.4.2.

Root cause

Two missing error boundaries in the plugin contract loading path:

1. resolvePluginDoctorContracts (config loader)

The Slack plugin's contract-api.js uses ESM imports. When loaded by jiti (CJS-compat), the imports become lazy getters on the module object. These getters throw when the underlying ESM resolution fails in the jiti context.

The current code wraps only the getJiti()() call in try/catch, but the property access on the returned module is outside the catch:

// Current (config-DMMR1XE_.js:214-221)
let mod;
try {
    mod = getJiti(contractSource)(contractSource);
} catch {
    continue;  // catches load errors
}
// These lines throw when jiti lazy getters fail:
const rules = coerceLegacyConfigRules(mod.default?.legacyConfigRules ?? mod.legacyConfigRules);
const normalizeCompatibilityConfig = ...

2. getBundledChannelSetupPlugin (bundled plugin loader)

The Telegram plugin's setup-entry.js references ./src/channel.setup.js which doesn't exist in the built package. The load call uses try/finally without a catch:

// Current (bundled-BUZYiQLp.js:256-262)
try {
    const plugin = entry.loadSetupPlugin();
    state.setupPluginsById.set(id, plugin);
    return plugin;
} finally {
    setupPluginLoadInProgressIds.delete(id);
}

Fix

Fix 1: Extend try/catch to cover lazy getter access

let mod;
let rules;
let normalizeCompatibilityConfig;
try {
    mod = getJiti(contractSource)(contractSource);
    rules = coerceLegacyConfigRules(mod.default?.legacyConfigRules ?? mod.legacyConfigRules);
    normalizeCompatibilityConfig = coerceNormalizeCompatibilityConfig(
        mod.normalizeCompatibilityConfig ?? mod.default?.normalizeCompatibilityConfig
    );
} catch {
    continue;
}

Fix 2: Add catch block for setup plugin loading

try {
    const plugin = entry.loadSetupPlugin();
    state.setupPluginsById.set(id, plugin);
    return plugin;
} catch {
    return;
} finally {
    setupPluginLoadInProgressIds.delete(id);
}

Environment

  • OpenClaw 2026.4.7 and 2026.4.8
  • Node.js 25.8.0
  • macOS (darwin)
  • Gateway runs as LaunchAgent on loopback

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions