From 3500a0bffa30e7f9a7f4197af369302f1621277c Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 9 May 2026 07:48:48 +0800 Subject: [PATCH] fix(cli): improve guardrail messages --- src/cli/program/config-guard.test.ts | 3 ++- src/cli/program/config-guard.ts | 10 ++++++++-- src/cli/root-guard.test.ts | 3 ++- src/cli/root-guard.ts | 9 ++++++--- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/cli/program/config-guard.test.ts b/src/cli/program/config-guard.test.ts index 5d12eb201aa..99f31de3d81 100644 --- a/src/cli/program/config-guard.test.ts +++ b/src/cli/program/config-guard.test.ts @@ -128,8 +128,9 @@ describe("ensureConfigReady", () => { setInvalidSnapshot(); const runtime = await runEnsureConfigReady(["message"]); - expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("Config invalid")); + expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("config is invalid")); expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("doctor --fix")); + expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("config validate")); expect(runtime.exit).toHaveBeenCalledWith(1); }); diff --git a/src/cli/program/config-guard.ts b/src/cli/program/config-guard.ts index fff5b9051ac..9a4eedc3c20 100644 --- a/src/cli/program/config-guard.ts +++ b/src/cli/program/config-guard.ts @@ -112,7 +112,7 @@ export async function ensureConfigReady(params: { const heading = (value: string) => colorize(rich, theme.heading, value); const commandText = (value: string) => colorize(rich, theme.command, value); - params.runtime.error(heading("Config invalid")); + params.runtime.error(heading("OpenClaw config is invalid")); params.runtime.error(`${muted("File:")} ${muted(shortenHomePath(snapshot.path))}`); if (issues.length > 0) { params.runtime.error(muted("Problem:")); @@ -124,7 +124,13 @@ export async function ensureConfigReady(params: { } params.runtime.error(""); params.runtime.error( - `${muted("Run:")} ${commandText(formatCliCommand("openclaw doctor --fix"))}`, + `${muted("Fix:")} ${commandText(formatCliCommand("openclaw doctor --fix"))}`, + ); + params.runtime.error( + `${muted("Inspect:")} ${commandText(formatCliCommand("openclaw config validate"))}`, + ); + params.runtime.error( + muted("Status, health, logs, and doctor commands still run with invalid config."), ); if (!allowInvalid) { params.runtime.exit(1); diff --git a/src/cli/root-guard.test.ts b/src/cli/root-guard.test.ts index aa539ca5951..8f419fe185e 100644 --- a/src/cli/root-guard.test.ts +++ b/src/cli/root-guard.test.ts @@ -91,6 +91,7 @@ describe("assertNotRoot", () => { process.getuid = () => 0; assertNotRoot({}); const output = stderrSpy.mock.calls.map((c) => String(c[0])).join(""); - expect(output).toContain("non-root user"); + expect(output).toContain("service user"); + expect(output).toContain("sudo -u -H openclaw"); }); }); diff --git a/src/cli/root-guard.ts b/src/cli/root-guard.ts index 81ec7447a40..3ce49693c3b 100644 --- a/src/cli/root-guard.ts +++ b/src/cli/root-guard.ts @@ -26,13 +26,16 @@ export function assertNotRoot(env: NodeJS.ProcessEnv = process.env): void { process.stderr.write( "[openclaw] Refusing to run as root.\n" + "\n" + - "Running the CLI as root causes:\n" + + "Why this is blocked:\n" + " - A separate state directory under /root/.openclaw/ instead of the service user's\n" + " - Conflicting systemd user services that race on port 18789\n" + " - Root-owned files in the service user's state dir (EACCES errors)\n" + "\n" + - "Run as a non-root user (e.g. su - ),\n" + - "or override this check:\n" + + "What to do:\n" + + " - Re-run as the service user: sudo -u -H openclaw ...\n" + + " - Or switch shells first: su - \n" + + "\n" + + "Intentional container/CI run only:\n" + " OPENCLAW_ALLOW_ROOT=1 openclaw ...\n", ); process.exit(1);