fix(workspaces): surface real error messages on failed workspace operations (#29167)

Signed-off-by: James Murdza <james@jamesmurdza.com>
This commit is contained in:
James Murdza
2026-05-29 14:00:23 -07:00
committed by GitHub
parent 7342e94094
commit 9d5f3c1833
5 changed files with 84 additions and 27 deletions

View File

@@ -27,6 +27,16 @@ export class ApiWorkspaceWarpError extends Schema.ErrorClass<ApiWorkspaceWarpErr
{ httpApiStatus: 400 },
) {}
export class ApiWorkspaceCreateError extends Schema.ErrorClass<ApiWorkspaceCreateError>("WorkspaceCreateError")(
{
name: Schema.Literal("WorkspaceCreateError"),
data: Schema.Struct({
message: Schema.String,
}),
},
{ httpApiStatus: 400 },
) {}
export const WorkspacePaths = {
adapters: `${root}/adapter`,
list: root,
@@ -64,7 +74,7 @@ export const WorkspaceApi = HttpApi.make("workspace")
query: WorkspaceRoutingQuery,
payload: CreatePayload,
success: described(Workspace.Info, "Workspace created"),
error: HttpApiError.BadRequest,
error: [ApiWorkspaceCreateError, HttpApiError.BadRequest],
}).annotateMerge(
OpenApi.annotations({
identifier: "experimental.workspace.create",

View File

@@ -2,12 +2,12 @@ import { listAdapters } from "@/control-plane/adapters"
import { Workspace } from "@/control-plane/workspace"
import * as InstanceState from "@/effect/instance-state"
import { Vcs } from "@/project/vcs"
import { Effect } from "effect"
import { HttpApiBuilder, HttpApiError } from "effect/unstable/httpapi"
import { Cause, Effect } from "effect"
import { HttpApiBuilder } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../api"
import { notFound } from "../errors"
import { ApiVcsApplyError } from "../groups/instance"
import { ApiWorkspaceWarpError, CreatePayload, WarpPayload } from "../groups/workspace"
import { ApiWorkspaceCreateError, ApiWorkspaceWarpError, CreatePayload, WarpPayload } from "../groups/workspace"
export const workspaceHandlers = HttpApiBuilder.group(InstanceHttpApi, "workspace", (handlers) =>
Effect.gen(function* () {
@@ -30,7 +30,22 @@ export const workspaceHandlers = HttpApiBuilder.group(InstanceHttpApi, "workspac
extra: ctx.payload.extra ?? null,
projectID: instance.project.id,
})
.pipe(Effect.mapError(() => new HttpApiError.BadRequest({})))
.pipe(
Effect.catchCause((cause) => {
// Plugin throws surface as defects (because EffectBridge.fromPromise uses Effect.promise),
// bypassing Effect.mapError. Walk the cause to surface the real error to the client.
const die = cause.reasons.find(Cause.isDieReason)
const fail = cause.reasons.find(Cause.isFailReason)
const reason: unknown = die?.defect ?? fail?.error
const message = reason instanceof Error ? reason.message : "Workspace creation failed"
return Effect.fail(
new ApiWorkspaceCreateError({
name: "WorkspaceCreateError",
data: { message },
}),
)
}),
)
})
const syncList = Effect.fn("WorkspaceHttpApi.syncList")(function* () {