FN-5832: surface unsupported message-role provider errors
Make unsupported provider message-role failures fail fast with actionable operator guidance. - detect provider errors that reject `messages.[n].role` values via a dedicated transient-error detector helper - classify unsupported message-role failures as operator-actionable/permanent to prevent retry loops - annotate `promptSessionAndCheck` failures with model/provider compatibility guidance for imported company-role agents - add regression tests for role-error detection, operator-actionable classification, and prompt-boundary error hinting - add a patch changeset for `@runfusion/fusion` Files changed: .changeset/fn-5832-unsupported-message-role.md | 5 +++ .../pi-prompt-session-and-check-recursion.test.ts | 24 +++++++++++++++ .../src/__tests__/transient-error-detector.test.ts | 36 ++++++++++++++++++++++ packages/engine/src/pi.ts | 8 +++++ packages/engine/src/transient-error-detector.ts | 14 ++++++++- 5 files changed, 86 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-5832 Fusion-Task-Lineage: b3f78f0e-d097-4cb7-a652-dc547f46fe1d
This commit is contained in:
@@ -56,4 +56,28 @@ describe("promptSessionAndCheck recursion guard (FN-4930)", () => {
|
||||
}
|
||||
expect(warnMock).not.toHaveBeenCalledWith(expect.stringContaining("failed to inspect transcript"));
|
||||
});
|
||||
|
||||
it("annotates unsupported message-role provider errors with actionable hint", async () => {
|
||||
const { promptSessionAndCheck } = await import("../pi.js");
|
||||
|
||||
const state = {
|
||||
errorMessage: "",
|
||||
messages: [],
|
||||
};
|
||||
|
||||
const session = {
|
||||
prompt: vi.fn(async () => {
|
||||
state.errorMessage =
|
||||
"developer is not one of ['system', 'assistant', 'user', 'tool', 'function'] - 'messages.[0].role'";
|
||||
}),
|
||||
state,
|
||||
} as any;
|
||||
|
||||
await expect(promptSessionAndCheck(session, "hello")).rejects.toThrow(
|
||||
/developer is not one of \['system', 'assistant', 'user', 'tool', 'function'\] - 'messages\.\[0\]\.role'/,
|
||||
);
|
||||
await expect(promptSessionAndCheck(session, "hello")).rejects.toThrow(
|
||||
/Operator action required: this agent's configured model\/provider rejected a message role\./,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
extractMissingModulePath,
|
||||
isOperatorActionableAgentError,
|
||||
isStaleWorktreeModuleResolutionError,
|
||||
isUnsupportedMessageRoleError,
|
||||
TRANSIENT_ERROR_PATTERNS,
|
||||
} from "../transient-error-detector.js";
|
||||
import { isUsageLimitError } from "../usage-limit-detector.js";
|
||||
@@ -269,6 +270,34 @@ describe("Transient Error Detector", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("isUnsupportedMessageRoleError", () => {
|
||||
it("returns true for the reported provider error", () => {
|
||||
expect(
|
||||
isUnsupportedMessageRoleError(
|
||||
"developer is not one of ['system', 'assistant', 'user', 'tool', 'function'] - 'messages.[0].role'",
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for role/index/case variants", () => {
|
||||
expect(
|
||||
isUnsupportedMessageRoleError(
|
||||
"assistant_role is not one of ['system','assistant','user','tool','function'] - 'messages.[3].role'",
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isUnsupportedMessageRoleError(
|
||||
"'MESSAGES.[9].ROLE' IS NOT ONE OF ['system','assistant']",
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for unrelated errors", () => {
|
||||
expect(isUnsupportedMessageRoleError("socket hang up")).toBe(false);
|
||||
expect(isUnsupportedMessageRoleError("invalid api key")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isOperatorActionableAgentError", () => {
|
||||
it("returns true for credential/model/billing errors", () => {
|
||||
expect(isOperatorActionableAgentError("invalid api key")).toBe(true);
|
||||
@@ -278,6 +307,13 @@ describe("Transient Error Detector", () => {
|
||||
expect(isOperatorActionableAgentError("billing issue: quota exceeded")).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for unsupported message-role errors", () => {
|
||||
const message =
|
||||
"developer is not one of ['system', 'assistant', 'user', 'tool', 'function'] - 'messages.[0].role'";
|
||||
expect(isOperatorActionableAgentError(message)).toBe(true);
|
||||
expect(classifyError(message)).toBe("permanent");
|
||||
});
|
||||
|
||||
it("returns false for transient network errors", () => {
|
||||
expect(isOperatorActionableAgentError("socket hang up")).toBe(false);
|
||||
expect(isOperatorActionableAgentError("upstream connect error")).toBe(false);
|
||||
|
||||
Reference in New Issue
Block a user