





















@@ -1,6 +1,6 @@
11import path from "node:path";
22import type { OpenClawConfig } from "../../config/types.openclaw.js";
3-import { formatErrorMessage } from "../../infra/errors.js";
3+import { extractErrorCode, formatErrorMessage } from "../../infra/errors.js";
44import { isPathInside } from "../../infra/path-guards.js";
55import { createSubsystemLogger } from "../../logging/subsystem.js";
66import type {
@@ -241,6 +241,35 @@ function loadGeneratedBundledChannelModule(params: {
241241}
242242}
243243244+// Walk the `.cause` chain looking for a Node-style "module not found" code.
245+// Native-require failures inside `module-loader.ts` rewrap the original Node
246+// error in a new Error with `{ cause }`, so the missing-module code lives on
247+// the cause rather than the top-level error.
248+function findMissingModuleCodeInChain(error: unknown): string | undefined {
249+const seen = new Set<unknown>();
250+let current: unknown = error;
251+while (current && !seen.has(current)) {
252+seen.add(current);
253+const code = extractErrorCode(current);
254+if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
255+return code;
256+}
257+if (typeof current !== "object") {
258+return undefined;
259+}
260+current = (current as { cause?: unknown }).cause;
261+}
262+return undefined;
263+}
264+265+export function describeBundledChannelLoadError(error: unknown, channelId: string): string {
266+const detail = formatErrorMessage(error);
267+if (findMissingModuleCodeInChain(error) !== undefined) {
268+return `${detail} (run \`openclaw doctor --fix\` to install missing bundled runtime dependencies for channel ${channelId})`;
269+}
270+return detail;
271+}
272+244273function loadGeneratedBundledChannelEntry(params: {
245274rootScope: BundledChannelRootScope;
246275metadata: BundledChannelPluginMetadata;
@@ -264,7 +293,7 @@ function loadGeneratedBundledChannelEntry(params: {
264293 entry,
265294};
266295} catch (error) {
267-const detail = formatErrorMessage(error);
296+const detail = describeBundledChannelLoadError(error, params.metadata.manifest.id);
268297log.warn(`[channels] failed to load bundled channel ${params.metadata.manifest.id}: ${detail}`);
269298return null;
270299}
@@ -293,7 +322,7 @@ function loadGeneratedBundledChannelSetupEntry(params: {
293322}
294323return setupEntry;
295324} catch (error) {
296-const detail = formatErrorMessage(error);
325+const detail = describeBundledChannelLoadError(error, params.metadata.manifest.id);
297326log.warn(
298327`[channels] failed to load bundled channel setup entry ${params.metadata.manifest.id}: ${detail}`,
299328);
@@ -573,7 +602,7 @@ function getBundledChannelPluginForRoot(
573602loadContext.lazyPluginsById.set(id, normalizedPlugin);
574603return normalizedPlugin;
575604} catch (error) {
576-const detail = formatErrorMessage(error);
605+const detail = describeBundledChannelLoadError(error, id);
577606log.warn(`[channels] failed to load bundled channel ${id}: ${detail}`);
578607loadContext.lazyPluginsById.set(id, null);
579608return undefined;
@@ -601,7 +630,7 @@ function getBundledChannelSecretsForRoot(
601630loadContext.lazySecretsById.set(id, secrets ?? null);
602631return secrets;
603632} catch (error) {
604-const detail = formatErrorMessage(error);
633+const detail = describeBundledChannelLoadError(error, id);
605634log.warn(`[channels] failed to load bundled channel secrets ${id}: ${detail}`);
606635loadContext.lazySecretsById.set(id, null);
607636return undefined;
@@ -626,7 +655,7 @@ function getBundledChannelAccountInspectorForRoot(
626655loadContext.lazyAccountInspectorsById.set(id, inspector);
627656return inspector;
628657} catch (error) {
629-const detail = formatErrorMessage(error);
658+const detail = describeBundledChannelLoadError(error, id);
630659log.warn(`[channels] failed to load bundled channel account inspector ${id}: ${detail}`);
631660loadContext.lazyAccountInspectorsById.set(id, null);
632661return undefined;
@@ -654,7 +683,7 @@ function getBundledChannelSetupPluginForRoot(
654683loadContext.lazySetupPluginsById.set(id, plugin);
655684return plugin;
656685} catch (error) {
657-const detail = formatErrorMessage(error);
686+const detail = describeBundledChannelLoadError(error, id);
658687log.warn(`[channels] failed to load bundled channel setup ${id}: ${detail}`);
659688loadContext.lazySetupPluginsById.set(id, null);
660689return undefined;
@@ -682,7 +711,7 @@ function getBundledChannelSetupSecretsForRoot(
682711loadContext.lazySetupSecretsById.set(id, secrets ?? null);
683712return secrets;
684713} catch (error) {
685-const detail = formatErrorMessage(error);
714+const detail = describeBundledChannelLoadError(error, id);
686715log.warn(`[channels] failed to load bundled channel setup secrets ${id}: ${detail}`);
687716loadContext.lazySetupSecretsById.set(id, null);
688717return undefined;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。