From b46cec2a7d2186c83e046cbfbf166b9b4b51ee98 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Mon, 25 May 2026 15:57:12 -0400 Subject: [PATCH] fix(httpapi): model optional worktree payload as no content (#29246) --- .../instance/httpapi/groups/experimental.ts | 4 ++-- .../instance/httpapi/handlers/experimental.ts | 4 ++-- .../server/worktree-endpoint-repro.test.ts | 22 +++++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts index 4cda970e87..90be9f218f 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts @@ -5,7 +5,7 @@ import { Session } from "@/session/session" import { Worktree } from "@/worktree" import { NonNegativeInt } from "@opencode-ai/core/schema" import { Schema } from "effect" -import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" +import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi" import { Authorization } from "../middleware/authorization" import { InstanceContextMiddleware } from "../middleware/instance-context" import { @@ -168,7 +168,7 @@ export const ExperimentalApi = HttpApi.make("experimental") HttpApiEndpoint.post("worktreeCreate", ExperimentalPaths.worktree, { disableCodecs: true, query: WorkspaceRoutingQuery, - payload: Schema.UndefinedOr(Worktree.CreateInput), + payload: [HttpApiSchema.NoContent, Worktree.CreateInput], success: described(Worktree.Info, "Worktree created"), error: WorktreeApiError, }).annotateMerge( diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts index 56a8de3ffa..4061bec298 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts @@ -104,9 +104,9 @@ export const experimentalHandlers = HttpApiBuilder.group(InstanceHttpApi, "exper }) const worktreeCreate = Effect.fn("ExperimentalHttpApi.worktreeCreate")(function* (ctx: { - payload: Worktree.CreateInput | undefined + payload: typeof Worktree.CreateInput.Type | void }) { - return yield* mapWorktreeError(worktreeSvc.create(ctx.payload)) + return yield* mapWorktreeError(worktreeSvc.create(ctx.payload ?? undefined)) }) const worktreeRemove = Effect.fn("ExperimentalHttpApi.worktreeRemove")(function* (input: { diff --git a/packages/opencode/test/server/worktree-endpoint-repro.test.ts b/packages/opencode/test/server/worktree-endpoint-repro.test.ts index 747393bbd2..9f73cd8a4f 100644 --- a/packages/opencode/test/server/worktree-endpoint-repro.test.ts +++ b/packages/opencode/test/server/worktree-endpoint-repro.test.ts @@ -228,6 +228,28 @@ describe("worktree endpoint reproduction", () => { { git: true }, ) + worktreeTest( + "direct HttpApi worktree create rejects explicit null payload", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const server = yield* serverScoped() + + const response = yield* request( + server, + `${ExperimentalPaths.worktree}?directory=${encodeURIComponent(test.directory)}`, + { + method: "POST", + headers: { "content-type": "application/json" }, + body: "null", + }, + ) + + expect(response.status).toBe(400) + }), + { git: true }, + ) + worktreeTest( "workspace worktree create does not hang", () =>