Skip to content

Commit 28c852f

Browse files
committed
fix(gateway): log secrets handler error details
1 parent a0c3cd6 commit 28c852f

3 files changed

Lines changed: 44 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Docs: https://docs.openclaw.ai
3030

3131
### Fixes
3232

33+
- Gateway/secrets: include the caught error message in `secrets.reload` and `secrets.resolve` warning logs while keeping RPC errors generic, so operators can diagnose reload and permission failures. Thanks @davidangularme.
3334
- fix(infra): block workspace state-directory env override [AI]. (#75940) Thanks @pgondhi987.
3435
- MCP/OpenAI: normalize parameter-free tool schemas whose top-level object `properties` is missing, null, or invalid before sending tools to OpenAI, so MCP tools without params stay usable. Fixes #75362. Thanks @tolkonepiu and @SymbolStar.
3536
- TTS: honor explicit short `[[tts:text]]...[[/tts:text]]` blocks while keeping untagged short auto-TTS suppressed, so tagged voice replies are synthesized instead of being dropped as empty voice-only payloads. Fixes #73758. Thanks @yfge.

src/gateway/server-methods/secrets.test.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ describe("secrets handlers", () => {
5050
diagnostics: string[];
5151
inactiveRefPaths: string[];
5252
}>;
53+
log?: { warn?: (message: string) => void };
5354
}) {
5455
const reloadSecrets = overrides?.reloadSecrets ?? (async () => ({ warningCount: 0 }));
5556
const resolveSecrets =
@@ -62,6 +63,7 @@ describe("secrets handlers", () => {
6263
return createSecretsHandlers({
6364
reloadSecrets,
6465
resolveSecrets,
66+
log: overrides?.log,
6567
});
6668
}
6769

@@ -75,8 +77,10 @@ describe("secrets handlers", () => {
7577
});
7678

7779
it("returns unavailable when reload fails", async () => {
80+
const warn = vi.fn();
7881
const handlers = createHandlers({
79-
reloadSecrets: vi.fn().mockRejectedValue(new Error("reload failed")),
82+
reloadSecrets: vi.fn().mockRejectedValue(new Error("disk full")),
83+
log: { warn },
8084
});
8185
const respond = vi.fn();
8286
await invokeSecretsReload({ handlers, respond });
@@ -88,6 +92,7 @@ describe("secrets handlers", () => {
8892
message: "secrets.reload failed",
8993
}),
9094
);
95+
expect(warn).toHaveBeenCalledWith(expect.stringContaining("disk full"));
9196
});
9297

9398
it("resolves requested command secret assignments from the active snapshot", async () => {
@@ -189,12 +194,39 @@ describe("secrets handlers", () => {
189194
});
190195

191196
it("returns unavailable when secrets.resolve handler returns an invalid payload shape", async () => {
197+
const warn = vi.fn();
192198
const resolveSecrets = vi.fn().mockResolvedValue({
193199
assignments: [{ path: TALK_TEST_PROVIDER_API_KEY_PATH, pathSegments: [""], value: "sk" }],
194200
diagnostics: [],
195201
inactiveRefPaths: [],
196202
});
197-
const handlers = createHandlers({ resolveSecrets });
203+
const handlers = createHandlers({ resolveSecrets, log: { warn } });
204+
const respond = vi.fn();
205+
await invokeSecretsResolve({
206+
handlers,
207+
respond,
208+
commandName: "memory status",
209+
targetIds: ["talk.providers.*.apiKey"],
210+
});
211+
expect(respond).toHaveBeenCalledWith(
212+
false,
213+
undefined,
214+
expect.objectContaining({
215+
code: "UNAVAILABLE",
216+
message: "secrets.resolve failed",
217+
}),
218+
);
219+
expect(warn).toHaveBeenCalledWith(
220+
expect.stringContaining("secrets.resolve returned invalid payload."),
221+
);
222+
});
223+
224+
it("logs error details when secrets.resolve throws", async () => {
225+
const warn = vi.fn();
226+
const handlers = createHandlers({
227+
resolveSecrets: vi.fn().mockRejectedValue(new Error("EACCES: permission denied")),
228+
log: { warn },
229+
});
198230
const respond = vi.fn();
199231
await invokeSecretsResolve({
200232
handlers,
@@ -210,5 +242,6 @@ describe("secrets handlers", () => {
210242
message: "secrets.resolve failed",
211243
}),
212244
);
245+
expect(warn).toHaveBeenCalledWith(expect.stringContaining("EACCES: permission denied"));
213246
});
214247
});

src/gateway/server-methods/secrets.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import {
88
} from "../protocol/index.js";
99
import type { GatewayRequestHandlers } from "./types.js";
1010

11+
function errorMessage(error: unknown): string {
12+
return error instanceof Error ? error.message : String(error);
13+
}
14+
1115
function invalidSecretsResolveField(
1216
errors: ErrorObject[] | null | undefined,
1317
): "commandName" | "targetIds" {
@@ -43,8 +47,8 @@ export function createSecretsHandlers(params: {
4347
try {
4448
const result = await params.reloadSecrets();
4549
respond(true, { ok: true, warningCount: result.warningCount });
46-
} catch {
47-
params.log?.warn?.("secrets.reload failed");
50+
} catch (error) {
51+
params.log?.warn?.(`secrets.reload failed: ${errorMessage(error)}`);
4852
respond(false, undefined, errorShape(ErrorCodes.UNAVAILABLE, "secrets.reload failed"));
4953
}
5054
},
@@ -100,8 +104,8 @@ export function createSecretsHandlers(params: {
100104
throw new Error("secrets.resolve returned invalid payload.");
101105
}
102106
respond(true, payload);
103-
} catch {
104-
params.log?.warn?.("secrets.resolve failed");
107+
} catch (error) {
108+
params.log?.warn?.(`secrets.resolve failed: ${errorMessage(error)}`);
105109
respond(false, undefined, errorShape(ErrorCodes.UNAVAILABLE, "secrets.resolve failed"));
106110
}
107111
},

0 commit comments

Comments
 (0)