fix(knowledge_fs): enhance error handling and logging for source compilation failures

This commit is contained in:
FFXN 2026-08-08 16:18:23 +08:00
parent 86bfb66990
commit 757c7227f8
4 changed files with 69 additions and 2 deletions

View File

@ -155,6 +155,25 @@ describe("Source compilation publication executor", () => {
},
);
it("preserves the durable compilation failure detail", async () => {
const fixture = compilationJobs([
compilationJob({
error: "Embedding model is unavailable",
runState: "failed",
stage: "failed",
}),
]);
const executor = createSourceCompilationPublicationExecutor({
compilationJobs: fixture.jobs,
maxWaitMs: 20,
pollIntervalMs: 1,
});
await expect(executor.publishAndWait(compilationInput())).rejects.toThrow(
"Source compilation terminated as failed: Embedding model is unavailable",
);
});
it("fails closed when the durable attempt disappears and tolerates cancellation cleanup failure", async () => {
const fixture = compilationJobs([null], { cancelError: new Error("cancel unavailable") });
const executor = createSourceCompilationPublicationExecutor({

View File

@ -141,8 +141,9 @@ export function createSourceCompilationPublicationExecutor({
current.runState === "canceled" ||
current.runState === "superseded"
) {
const detail = current.error ? `: ${current.error}` : "";
throw new LogicalDocumentValidationError(
`Source compilation terminated as ${current.runState}`,
`Source compilation terminated as ${current.runState}${detail}`,
);
}
await cancellableDelay(pollIntervalMs, input.signal);

View File

@ -834,6 +834,42 @@ describe("source-product workflow provider imports", () => {
}
});
it("logs logical-revision publication failures and persists a specific workflow error", async () => {
const consoleError = vi.spyOn(console, "error").mockImplementation(() => undefined);
const source = sourceRecord("publication-failure-source", { type: "connector" });
const fixture = await createFixture({
inventory: [],
onlineDocuments: { getPageContent: vi.fn(async () => ({ content: "content" })) },
publishError: new Error("Source compilation terminated as failed: embedding unavailable"),
run: providerRun(source.id, "online-document-import", {
items: [
{
pageId: "publication-page",
providerItemId: "publication-provider-item",
type: "page",
workspaceId: "publication-workspace",
},
],
}),
source,
});
await expect(fixture.runtime.tick()).resolves.toMatchObject({ completed: 0, failed: 1 });
await expect(fixture.getRun()).resolves.toMatchObject({
lastErrorCode: "SOURCE_DOCUMENT_COMPILATION_FAILED",
lastErrorMessage: "Source document compilation failed",
state: "failed",
});
expect(consoleError).toHaveBeenCalledWith(
"Source workflow logical revision publication failed",
expect.objectContaining({
error: "Source compilation terminated as failed: embedding unavailable",
sourceId: source.id,
}),
);
consoleError.mockRestore();
});
it("runs the explicit crawl-import kind and cleans an empty frozen selection", async () => {
const deleteRun = vi.fn(async () => ({ deleted: 0, hasMore: false }));
const source = sourceRecord("empty-crawl-import", { type: "web" });

View File

@ -1350,13 +1350,24 @@ async function publishLogicalRevisions(
),
);
} catch (error) {
console.error("Source workflow logical revision publication failed", {
error: error instanceof Error ? error.message : String(error),
filename: candidate.filename,
knowledgeSpaceId: run.knowledgeSpaceId,
sourceId: source.id,
workflowId: run.id,
workflowKind: run.kind,
});
await input.materializer.compensate({
documents: [document],
knowledgeSpaceId: run.knowledgeSpaceId,
sourceId: source.id,
tenantId: run.tenantId,
});
throw error;
throw runtimeError(
"SOURCE_DOCUMENT_COMPILATION_FAILED",
"Source document compilation failed",
);
}
if (publication.kind === "unchanged") {
await input.materializer.compensate({