mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-23 17:33:51 +02:00
Preserve cloud upstream error details
This commit is contained in:
1 parent
034733e444
commit
2c3217bcd3
2 files changed
+56
-3
No files matched your search
@@ -0,0 +1,26 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { HttpError } from "../errors.js";
|
||||
import { cloudUpstreamRemoteFailureReport } from "../services/cloud-upstreams.js";
|
||||
|
||||
describe("cloud upstream remote failures", () => {
|
||||
it("preserves the cloud response body and message on run reports", () => {
|
||||
const body = {
|
||||
error: "bad_request",
|
||||
message: "entities[42].body must be an object",
|
||||
errors: [{ path: "entities[42].body" }],
|
||||
};
|
||||
|
||||
expect(cloudUpstreamRemoteFailureReport(new HttpError(400, "bad_request", body))).toEqual({
|
||||
error: "bad_request",
|
||||
errorMessage: "entities[42].body must be an object",
|
||||
details: body,
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to the thrown error message for non-remote failures", () => {
|
||||
expect(cloudUpstreamRemoteFailureReport(new Error("network failed"))).toEqual({
|
||||
error: "network failed",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
projects,
|
||||
routines,
|
||||
} from "@paperclipai/db";
|
||||
import { badRequest, notFound } from "../errors.js";
|
||||
import { badRequest, HttpError, notFound } from "../errors.js";
|
||||
import { companyPortabilityService } from "./company-portability.js";
|
||||
|
||||
const DEFAULT_SCOPES = ["upstream_import:preview", "upstream_import:write", "upstream_import:read"];
|
||||
@@ -380,13 +380,14 @@ export function cloudUpstreamService(db: Db, options: { instanceId?: string } =
|
||||
return finalRun;
|
||||
} catch (error) {
|
||||
const failedAt = new Date();
|
||||
const failure = cloudUpstreamRemoteFailureReport(error);
|
||||
return updateRun(runId, {
|
||||
status: "failed",
|
||||
activeStep: "push",
|
||||
progressPercent: 100,
|
||||
events: [
|
||||
...initialEvents,
|
||||
event(failedAt.toISOString(), "push", "failed", error instanceof Error ? error.message : String(error)),
|
||||
event(failedAt.toISOString(), "push", "failed", failure.errorMessage ?? failure.error),
|
||||
],
|
||||
report: {
|
||||
runId,
|
||||
@@ -394,7 +395,7 @@ export function cloudUpstreamService(db: Db, options: { instanceId?: string } =
|
||||
manifestHash: bundle.manifest.manifestHash,
|
||||
idempotencyKey: bundle.manifest.idempotencyKey,
|
||||
retryOfRunId: input.retryOfRunId ?? null,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
...failure,
|
||||
},
|
||||
completedAt: failedAt,
|
||||
});
|
||||
@@ -929,6 +930,32 @@ async function parseRemoteResponse(response: Response): Promise<unknown> {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function cloudUpstreamRemoteFailureReport(error: unknown): {
|
||||
error: string;
|
||||
errorMessage?: string;
|
||||
details?: unknown;
|
||||
} {
|
||||
const fallback = error instanceof Error ? error.message : String(error);
|
||||
if (!(error instanceof HttpError)) {
|
||||
return { error: fallback };
|
||||
}
|
||||
const remote = remoteErrorBody(error.details);
|
||||
return {
|
||||
error: remote.error ?? error.message,
|
||||
...(remote.message ? { errorMessage: remote.message } : {}),
|
||||
...(error.details !== undefined ? { details: error.details } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function remoteErrorBody(details: unknown): { error?: string; message?: string } {
|
||||
const record = optionalObject(details);
|
||||
if (!record) return {};
|
||||
return {
|
||||
error: optionalString(record.error) ?? undefined,
|
||||
message: optionalString(record.message) ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async function postJson<T>(url: string, body: unknown): Promise<T> {
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
|
||||
Reference in new issue
Block a user