fix(gateway): improve validation errors (#1347)
Thanks @vignesh07. Co-authored-by: Vignesh <vignesh07@users.noreply.github.com>
This commit is contained in:
@@ -29,6 +29,7 @@ Docs: https://docs.clawd.bot
|
||||
- Model catalog: avoid caching import failures, log transient discovery errors, and keep partial results. (#1332) — thanks @dougvk.
|
||||
- Doctor: clarify plugin auto-enable hint text in the startup banner.
|
||||
- Gateway: clarify unauthorized handshake responses with token/password mismatch guidance.
|
||||
- Gateway: clarify connect/validation errors for gateway params. (#1347) — thanks @vignesh07.
|
||||
- Gateway: preserve restart wake routing + thread replies across restarts. (#1337) — thanks @John-Rood.
|
||||
- Gateway: reschedule per-agent heartbeats on config hot reload without restarting the runner.
|
||||
- Config: log invalid config issues once per run and keep invalid-config errors stackless.
|
||||
|
||||
65
src/gateway/protocol/index.test.ts
Normal file
65
src/gateway/protocol/index.test.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { ErrorObject } from "ajv";
|
||||
|
||||
import { formatValidationErrors } from "./index.js";
|
||||
|
||||
const makeError = (overrides: Partial<ErrorObject>): ErrorObject => ({
|
||||
keyword: "type",
|
||||
instancePath: "",
|
||||
schemaPath: "#/",
|
||||
params: {},
|
||||
message: "validation error",
|
||||
...overrides,
|
||||
});
|
||||
|
||||
describe("formatValidationErrors", () => {
|
||||
it("returns unknown validation error when missing errors", () => {
|
||||
expect(formatValidationErrors(undefined)).toBe("unknown validation error");
|
||||
expect(formatValidationErrors(null)).toBe("unknown validation error");
|
||||
});
|
||||
|
||||
it("returns unknown validation error when errors list is empty", () => {
|
||||
expect(formatValidationErrors([])).toBe("unknown validation error");
|
||||
});
|
||||
|
||||
it("formats additionalProperties at root", () => {
|
||||
const err = makeError({
|
||||
keyword: "additionalProperties",
|
||||
params: { additionalProperty: "token" },
|
||||
});
|
||||
|
||||
expect(formatValidationErrors([err])).toBe("at root: unexpected property 'token'");
|
||||
});
|
||||
|
||||
it("formats additionalProperties with instancePath", () => {
|
||||
const err = makeError({
|
||||
keyword: "additionalProperties",
|
||||
instancePath: "/auth",
|
||||
params: { additionalProperty: "token" },
|
||||
});
|
||||
|
||||
expect(formatValidationErrors([err])).toBe("at /auth: unexpected property 'token'");
|
||||
});
|
||||
|
||||
it("formats message with path for other errors", () => {
|
||||
const err = makeError({
|
||||
keyword: "required",
|
||||
instancePath: "/auth",
|
||||
message: "must have required property 'token'",
|
||||
});
|
||||
|
||||
expect(formatValidationErrors([err])).toBe("at /auth: must have required property 'token'");
|
||||
});
|
||||
|
||||
it("de-dupes repeated entries", () => {
|
||||
const err = makeError({
|
||||
keyword: "required",
|
||||
instancePath: "/auth",
|
||||
message: "must have required property 'token'",
|
||||
});
|
||||
|
||||
expect(formatValidationErrors([err, err])).toBe(
|
||||
"at /auth: must have required property 'token'",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -310,7 +310,7 @@ export const validateWebLoginStartParams =
|
||||
export const validateWebLoginWaitParams = ajv.compile<WebLoginWaitParams>(WebLoginWaitParamsSchema);
|
||||
|
||||
export function formatValidationErrors(errors: ErrorObject[] | null | undefined) {
|
||||
if (!errors) return "unknown validation error";
|
||||
if (!errors?.length) return "unknown validation error";
|
||||
|
||||
const parts: string[] = [];
|
||||
|
||||
@@ -328,13 +328,18 @@ export function formatValidationErrors(errors: ErrorObject[] | null | undefined)
|
||||
}
|
||||
}
|
||||
|
||||
const message = typeof err?.message === "string" ? err.message : "validation error";
|
||||
const message =
|
||||
typeof err?.message === "string" && err.message.trim() ? err.message : "validation error";
|
||||
const where = instancePath ? `at ${instancePath}: ` : "";
|
||||
parts.push(`${where}${message}`);
|
||||
}
|
||||
|
||||
// De-dupe while preserving order.
|
||||
const unique = Array.from(new Set(parts));
|
||||
const unique = Array.from(new Set(parts.filter((part) => part.trim())));
|
||||
if (!unique.length) {
|
||||
const fallback = ajv.errorsText(errors, { separator: "; " });
|
||||
return fallback || "unknown validation error";
|
||||
}
|
||||
return unique.join("; ");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user