Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/commit-message-no-changes-error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@kilocode/cli": patch
"kilo-code": patch
---

Show a clear "No changes found to generate a commit message for" error instead of a generic "Unexpected server error" when there is nothing to commit. The endpoint now returns a typed 422, and the extension surfaces the real message directly.
2 changes: 1 addition & 1 deletion packages/kilo-vscode/src/services/commit-message/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export function registerCommitMessageService(
}
const msg = getErrorMessage(error)
console.error("[Kilo New] Failed to generate commit message:", msg)
vscode.window.showErrorMessage(`Failed to generate commit message: ${msg}`)
vscode.window.showErrorMessage(msg || "Failed to generate commit message. Please try again.")
})
},
)
Expand Down
9 changes: 8 additions & 1 deletion packages/opencode/src/kilocode/commit-message/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import { getGitContext } from "./git-context"

const log = Log.create({ service: "commit-message" })

export class NoChangesError extends Error {
constructor() {
super("No changes found to generate a commit message for")
this.name = "CommitMessageNoChanges"
}
}

export const CommitMessageRuntime = {
context(repoPath: string, selectedFiles?: string[]) {
return getGitContext(repoPath, selectedFiles)
Expand Down Expand Up @@ -146,7 +153,7 @@ const TIMEOUT_MS = 30_000
export async function generateCommitMessage(request: CommitMessageRequest): Promise<CommitMessageResponse> {
const ctx = await CommitMessageRuntime.context(request.path, request.selectedFiles)
if (ctx.files.length === 0) {
throw new Error("No changes found to generate a commit message for")
throw new NoChangesError()
}

log.info("generating", {
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/kilocode/commit-message/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { generateCommitMessage } from "./generate"
export { generateCommitMessage, NoChangesError } from "./generate"
export type { CommitMessageRequest, CommitMessageResponse, GitContext, FileChange } from "./types"
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ const CommitMessageResponse = Schema.Struct({
message: Schema.String,
})

export class CommitMessageNoChangesError extends Schema.ErrorClass<CommitMessageNoChangesError>(
"CommitMessageNoChangesError",
)(
{ message: Schema.String },
{ httpApiStatus: 422 },
) {}

export const CommitMessageApi = HttpApi.make("commit-message")
.add(
HttpApiGroup.make("commit-message")
Expand All @@ -32,7 +39,7 @@ export const CommitMessageApi = HttpApi.make("commit-message")
query: WorkspaceRoutingQuery,
payload: CommitMessagePayload,
success: described(CommitMessageResponse, "Generated commit message"),
error: HttpApiError.BadRequest,
error: [HttpApiError.BadRequest, CommitMessageNoChangesError],
}).annotateMerge(
OpenApi.annotations({
identifier: "commitMessage.generate",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { HttpApiBuilder } from "effect/unstable/httpapi"
import { EffectBridge } from "@/effect/bridge"
import { InstanceHttpApi } from "@/server/routes/instance/httpapi/api"
import { Config } from "@/config/config"
import { generateCommitMessage } from "@/kilocode/commit-message"
import { CommitMessagePayload } from "../groups/commit-message"
import { generateCommitMessage, NoChangesError } from "@/kilocode/commit-message"
import { CommitMessageNoChangesError, CommitMessagePayload } from "../groups/commit-message"

export const commitMessageHandlers = HttpApiBuilder.group(InstanceHttpApi, "commit-message", (handlers) =>
Effect.gen(function* () {
Expand All @@ -22,6 +22,13 @@ export const commitMessageHandlers = HttpApiBuilder.group(InstanceHttpApi, "comm
previousMessage: ctx.payload.previousMessage,
prompt,
}),
).pipe(
Effect.catchDefect((defect) => {
Comment thread
marius-kilocode marked this conversation as resolved.
if (defect instanceof NoChangesError) {
return Effect.fail(new CommitMessageNoChangesError({ message: defect.message }))
}
return Effect.die(defect)
}),
)
return { message: result.message }
})
Expand Down
13 changes: 8 additions & 5 deletions packages/opencode/test/kilocode/commit-message/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ mock.module("@opencode-ai/core/util/log", () => ({
}),
}))

import { CommitMessageRuntime, generateCommitMessage } from "../../../src/kilocode/commit-message/generate"
import { CommitMessageRuntime, generateCommitMessage, NoChangesError } from "../../../src/kilocode/commit-message/generate"

const context = spyOn(CommitMessageRuntime, "context").mockImplementation(async (repoPath, selectedFiles) => {
captured = { path: repoPath, selected: selectedFiles }
Expand Down Expand Up @@ -133,11 +133,14 @@ describe("commit-message.generate", () => {
})

describe("error on no changes", () => {
test("throws when no git changes are found", async () => {
test("throws NoChangesError when no git changes are found", async () => {
mockGitContext = { branch: "main", recentCommits: [], files: [] }
await expect(generateCommitMessage({ path: "/repo" })).rejects.toThrow(
"No changes found to generate a commit message for",
)
const err = await generateCommitMessage({ path: "/repo" }).catch((e) => e)
expect(err).toBeInstanceOf(NoChangesError)
if (err instanceof NoChangesError) {
expect(err.message).toBe("No changes found to generate a commit message for")
expect(err.name).toBe("CommitMessageNoChanges")
}
})
})

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { afterEach, describe, expect, test } from "bun:test"
import { Server } from "../../../src/server/server"
import { resetDatabase } from "../../fixture/db"
import { disposeAllInstances, tmpdir } from "../../fixture/fixture"

afterEach(async () => {
await disposeAllInstances()
await resetDatabase()
})

describe("commit-message httpapi", () => {
test("returns 422 with the real message when there are no changes", async () => {
await using tmp = await tmpdir({ git: true })

const res = await Server.Default().app.request("/commit-message", {
method: "POST",
headers: { "content-type": "application/json", "x-kilo-directory": tmp.path },
body: JSON.stringify({ path: tmp.path }),
})

expect(res.status).toBe(422)
expect(await res.json()).toEqual({ message: "No changes found to generate a commit message for" })
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,16 @@ export const kiloScenarios: Scenario[] = [
.post("/commit-message", "commitMessage.generate")
.at((ctx) => ({ path: "/commit-message", headers: ctx.headers(), body: {} }))
.status(400),
http.protected
.post("/commit-message", "commitMessage.generate")
.at((ctx) => ({ path: "/commit-message", headers: ctx.headers(), body: { path: directory(ctx) } }))
.json(422, (body) => {
object(body)
check(
body.message === "No changes found to generate a commit message for",
"no changes should surface a real 422 message, not a masked 500",
)
}),
http.protected
.post("/session/{sessionID}/branch-name", "branchName.generate")
.at((ctx) => ({
Expand Down
9 changes: 9 additions & 0 deletions packages/sdk/js/src/v2/gen/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1693,6 +1693,7 @@ export type Config = {
continue_loop_on_deny?: boolean
sandbox?: boolean
sandbox_restrict_network?: boolean
sandbox_writable_paths?: Array<string>
mcp_timeout?: number
policies?: Array<ConfigV2ExperimentalPolicy>
}
Expand Down Expand Up @@ -2412,6 +2413,10 @@ export type BackgroundProcessLogs = {
output: string
}

export type CommitMessageNoChangesError = {
message: string
}

export type ConfigOverlayResponse = {
scope: "global" | "project"
effective: Config
Expand Down Expand Up @@ -10101,6 +10106,10 @@ export type CommitMessageGenerateErrors = {
* BadRequest | InvalidRequestError
*/
400: EffectHttpApiErrorBadRequest | InvalidRequestError
/**
* CommitMessageNoChangesError
*/
422: CommitMessageNoChangesError
}

export type CommitMessageGenerateError = CommitMessageGenerateErrors[keyof CommitMessageGenerateErrors]
Expand Down
Loading