From 6b02b00e23fa171f2d46bb662c09884f0b34a559 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 25 Aug 2026 20:14:25 -0700 Subject: [PATCH] fix(gateway): scope crash loop channel recovery hints (#129697) --- src/infra/gateway-boot-lifecycle.test.ts | 24 +++++++++++++++++++++++- src/infra/gateway-boot-lifecycle.ts | 14 +++++++------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/infra/gateway-boot-lifecycle.test.ts b/src/infra/gateway-boot-lifecycle.test.ts index 809b41f8a3e1..e9c5f8879262 100644 --- a/src/infra/gateway-boot-lifecycle.test.ts +++ b/src/infra/gateway-boot-lifecycle.test.ts @@ -3,7 +3,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice"; -import { afterEach, describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it, vi } from "vitest"; import { formatLegacyAgentMediaMigrationRequiredMessage, GATEWAY_AGENT_MEDIA_MIGRATION_REQUIRED_REASON, @@ -35,6 +35,7 @@ const GATEWAY_BOOT_LIFECYCLE_RETENTION_MS = 24 * 60 * 60_000; afterEach(() => { closeOpenClawStateDatabaseForTest(); + vi.unstubAllEnvs(); }); function createLifecycleDb() { @@ -386,4 +387,25 @@ describe("formatGatewayCrashLoopManualChannelStartHint", () => { formatGatewayCrashLoopManualChannelStartHint({ channelId: "telegram", accountId: "work" }), ).toContain(`--params '{"channel":"telegram","accountId":"work"}'`); }); + + it.each([ + { name: "default", profile: "", container: "", command: "openclaw" }, + { name: "named profile", profile: "work", container: "", command: "openclaw --profile work" }, + { name: "container", profile: "", container: "demo", command: "openclaw --container demo" }, + { + name: "container and profile", + profile: "work", + container: "demo", + command: "openclaw --container demo", + }, + ])("targets the active gateway for $name", ({ profile, container, command }) => { + vi.stubEnv("OPENCLAW_PROFILE", profile); + vi.stubEnv("OPENCLAW_CONTAINER_HINT", container); + + expect( + formatGatewayCrashLoopManualChannelStartHint({ channelId: "telegram", accountId: "work" }), + ).toBe( + `Start a channel manually with: ${command} gateway call channels.start --params '{"channel":"telegram","accountId":"work"}'`, + ); + }); }); diff --git a/src/infra/gateway-boot-lifecycle.ts b/src/infra/gateway-boot-lifecycle.ts index 92ce03579cc1..fda6b7c86c48 100644 --- a/src/infra/gateway-boot-lifecycle.ts +++ b/src/infra/gateway-boot-lifecycle.ts @@ -1,6 +1,7 @@ // Persists gateway boot outcomes for supervisor crash-loop decisions. import { randomUUID } from "node:crypto"; import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice"; +import { formatCliCommand } from "../cli/command-format.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; import { OPENCLAW_AGENT_SCHEMA_VERSION } from "../state/openclaw-agent-db-contract.js"; import { @@ -38,13 +39,12 @@ export function formatGatewayCrashLoopManualChannelStartHint(target?: { channelId: string; accountId?: string; }): string { - const params = target - ? JSON.stringify({ - channel: target.channelId, - ...(target.accountId ? { accountId: target.accountId } : {}), - }) - : `{"channel":""}`; - return `Start a channel manually with: openclaw gateway call channels.start --params '${params}'`; + const params = JSON.stringify({ + channel: target?.channelId ?? "", + ...(target?.accountId ? { accountId: target.accountId } : {}), + }); + const command = formatCliCommand("openclaw gateway call channels.start"); + return `Start a channel manually with: ${command} --params '${params}'`; } const gatewayLifecycleLog = createSubsystemLogger("gateway/lifecycle");