diff --git a/server/src/__tests__/issues-service.test.ts b/server/src/__tests__/issues-service.test.ts index e90b0f1a4c..6baed72c3a 100644 --- a/server/src/__tests__/issues-service.test.ts +++ b/server/src/__tests__/issues-service.test.ts @@ -2451,6 +2451,44 @@ describeEmbeddedPostgres("issueService.list participantAgentId", () => { expect(comments.map((comment) => comment.id)).toEqual([latestCommentId]); }); + it("returns no comments for an anchor cursor that is not a UUID", async () => { + const companyId = randomUUID(); + const issueId = randomUUID(); + const commentId = randomUUID(); + + await db.insert(companies).values({ + id: companyId, + name: "Paperclip", + issuePrefix: `T${companyId.replace(/-/g, "").slice(0, 6).toUpperCase()}`, + requireBoardApprovalForNewAgents: false, + }); + + await db.insert(issues).values({ + id: issueId, + companyId, + title: "Malformed cursor issue", + status: "todo", + priority: "medium", + }); + + await db.insert(issueComments).values({ + id: commentId, + companyId, + issueId, + body: "Only comment", + createdAt: new Date("2026-03-26T10:00:00.000Z"), + updatedAt: new Date("2026-03-26T10:00:00.000Z"), + }); + + const comments = await svc.listComments(issueId, { + afterCommentId: commentId.slice(0, 8), + order: "asc", + limit: 50, + }); + + expect(comments).toEqual([]); + }); + it("lists user comments when derived run attribution scans a timestamp window", async () => { const companyId = randomUUID(); const agentId = randomUUID(); diff --git a/server/src/services/issues.ts b/server/src/services/issues.ts index 50ee3d6451..8d363ab17d 100644 --- a/server/src/services/issues.ts +++ b/server/src/services/issues.ts @@ -8796,6 +8796,8 @@ export function issueService(db: Db) { const conditions = [eq(issueComments.issueId, issueId)]; if (afterCommentId) { + // Guard: reject non-UUID cursors before hitting the DB to avoid Postgres type errors. + if (!isUuidLike(afterCommentId)) return []; const anchor = await db .select({ id: issueComments.id,