diff --git a/src/cli/config-cli.test.ts b/src/cli/config-cli.test.ts index 6b9034ef28e..9b9d81a45f1 100644 --- a/src/cli/config-cli.test.ts +++ b/src/cli/config-cli.test.ts @@ -529,7 +529,7 @@ describe("config cli", () => { await expect(runConfigCommand(["config", "validate"])).rejects.toThrow("__exit__:1"); - expect(mockError).toHaveBeenCalledWith(expect.stringContaining("Config invalid at")); + expect(mockError).toHaveBeenCalledWith(expect.stringContaining("config is invalid")); expect(mockError).toHaveBeenCalledWith( expect.stringContaining("agents.defaults.suppressToolErrorWarnings"), ); diff --git a/src/cli/config-cli.ts b/src/cli/config-cli.ts index afd02c780dd..042febb6152 100644 --- a/src/cli/config-cli.ts +++ b/src/cli/config-cli.ts @@ -235,7 +235,7 @@ function isPlainRecord(value: unknown): value is Record { } function formatDoctorHint(message: string): string { - return `Run \`${formatCliCommand("openclaw doctor")}\` ${message}`; + return `Run \`${formatCliCommand("openclaw doctor --fix")}\` ${message}`; } function formatUnsupportedSecretRefPolicyFailureMessage(issues: string[]): string { @@ -525,7 +525,7 @@ async function loadValidConfig(runtime: RuntimeEnv = defaultRuntime) { if (snapshot.valid) { return snapshot; } - runtime.error(`Config invalid at ${shortenHomePath(snapshot.path)}.`); + runtime.error(`OpenClaw config is invalid: ${shortenHomePath(snapshot.path)}`); for (const line of formatConfigIssueLines(snapshot.issues, "-", { normalizeRoot: true })) { runtime.error(line); } @@ -1662,7 +1662,11 @@ export async function runConfigGet(opts: { path: string; json?: boolean; runtime const redacted = redactConfigObject(snapshot.config); const res = getAtPath(redacted, parsedPath); if (!res.found) { - runtime.error(danger(`Config path not found: ${opts.path}`)); + runtime.error( + danger( + `Config path not found: ${opts.path}. Run ${formatCliCommand("openclaw config validate")} to inspect config shape.`, + ), + ); runtime.exit(1); return; } @@ -1696,7 +1700,11 @@ export async function runConfigUnset(opts: { path: string; runtime?: RuntimeEnv const next = structuredClone(snapshot.resolved) as Record; const unsetResult = unsetAtPath(next, parsedPath); if (!unsetResult.removed) { - runtime.error(danger(`Config path not found: ${opts.path}`)); + runtime.error( + danger( + `Config path not found: ${opts.path}. Nothing was changed. Run ${formatCliCommand("openclaw config get ")} first if you are unsure of the path.`, + ), + ); runtime.exit(1); return; } @@ -1763,6 +1771,9 @@ export async function runConfigValidate(opts: { json?: boolean; runtime?: Runtim writeRuntimeJson(runtime, { valid: false, path: outputPath, error: "file not found" }, 0); } else { runtime.error(danger(`Config file not found: ${shortPath}`)); + runtime.error( + `Create one with ${formatCliCommand("openclaw onboard")} or run ${formatCliCommand("openclaw doctor --fix")}.`, + ); } runtime.exit(1); return; @@ -1774,12 +1785,13 @@ export async function runConfigValidate(opts: { json?: boolean; runtime?: Runtim if (opts.json) { writeRuntimeJson(runtime, { valid: false, path: outputPath, issues }); } else { - runtime.error(danger(`Config invalid at ${shortPath}:`)); + runtime.error(danger(`OpenClaw config is invalid: ${shortPath}`)); for (const line of formatConfigIssueLines(issues, danger("×"), { normalizeRoot: true })) { runtime.error(` ${line}`); } runtime.error(""); runtime.error(formatDoctorHint("to repair, or fix the keys above manually.")); + runtime.error(`Inspect with ${formatCliCommand("openclaw config validate")}.`); } runtime.exit(1); return; diff --git a/src/commands/config-validation.ts b/src/commands/config-validation.ts index ec939841540..b0e453d05ab 100644 --- a/src/commands/config-validation.ts +++ b/src/commands/config-validation.ts @@ -21,8 +21,9 @@ export async function requireValidConfigFileSnapshot( snapshot.issues.length > 0 ? formatConfigIssueLines(snapshot.issues, "-").join("\n") : "Unknown validation issue."; - runtime.error(`Config invalid:\n${issues}`); - runtime.error(`Fix the config or run ${formatCliCommand("openclaw doctor")}.`); + runtime.error(`OpenClaw config is invalid: ${snapshot.path}\n${issues}`); + runtime.error(`Fix: ${formatCliCommand("openclaw doctor --fix")}`); + runtime.error(`Inspect: ${formatCliCommand("openclaw config validate")}`); runtime.exit(1); return null; } diff --git a/src/commands/docs.ts b/src/commands/docs.ts index 88bad3185fb..ef68df0449d 100644 --- a/src/commands/docs.ts +++ b/src/commands/docs.ts @@ -32,7 +32,9 @@ function resolveNodeRunner(): NodeRunner { if (hasBinary("npx")) { return { cmd: "npx", args: ["-y"] }; } - throw new Error("Missing pnpm or npx; install a Node package runner."); + throw new Error( + `Docs search needs pnpm or npx to run the docs search helper. Install pnpm, or run ${formatCliCommand("npm install -g pnpm")}.`, + ); } async function runNodeTool(tool: string, toolArgs: string[], options: ToolRunOptions = {}) {