Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code d4cc6a360c chore: improve monitoring for fix(ai-chat): use AGENT_NOT_FOUND instead of AGENT
Added a test case to the existing `agent-graphql-api-exception-handler.util.spec.ts` that verifies `AGENT_NOT_FOUND` maps to `ErrorCode.NOT_FOUND` (not `INTERNAL_SERVER_ERROR`). This ensures future regressions in the exception code mapping are caught in CI.

After deploying the fix, "Thread not found" errors will surface as `NOT_FOUND` (404) instead of `INTERNAL_SERVER_ERROR` (500) in Sentry, which should be filtered by default alert rules since 404s are expected user behavior.
2026-04-17 16:11:10 +00:00
Sonarly Claude Code 7f6486a0ae fix(ai-chat): use AGENT_NOT_FOUND instead of AGENT_EXECUTION_FAILED for missing threads
https://sonarly.com/issue/28252?type=bug

When a user queries an AI chat thread that doesn't exist (deleted, stale reference, or wrong userWorkspaceId), the server returns a 500 InternalServerError instead of a 404 NotFoundError because the code uses the wrong AgentExceptionCode.

Fix: Changed all "Thread not found" and "Queued message not found" throw sites in the AI chat module from `AgentExceptionCode.AGENT_EXECUTION_FAILED` to `AgentExceptionCode.AGENT_NOT_FOUND`.

**Why:** `AGENT_EXECUTION_FAILED` maps to `InternalServerError` (500) in the GraphQL exception handler. Missing threads/messages are a normal not-found condition, not an execution failure. `AGENT_NOT_FOUND` already exists and correctly maps to `NotFoundError` (404) in both the GraphQL handler and REST filter.

**7 sites fixed across 4 files:**
- `agent-chat.service.ts`: `getThreadById()` and `getMessagesForThread()`
- `agent-chat-streaming.service.ts`: `streamAgentChat()`
- `agent-chat.resolver.ts`: `sendChatMessage()`, `deleteQueuedChatMessage()` (2 checks: queued message + thread)
- `agent-chat-subscription.resolver.ts`: `onAgentChatEvent()`

This stops the `GetChatMessages` query (and all other chat operations) from producing 500 errors when a thread is not found, reducing Sentry noise and returning correct HTTP semantics to the frontend.
2026-04-17 16:11:09 +00:00
5 changed files with 22 additions and 7 deletions
@@ -32,4 +32,19 @@ describe('agentGraphqlApiExceptionHandler', () => {
);
expect(graphqlError.extensions.userFriendlyMessage).toBeDefined();
});
it('maps AGENT_NOT_FOUND to NOT_FOUND so missing threads return 404', () => {
const error = new AgentException(
'Thread not found',
AgentExceptionCode.AGENT_NOT_FOUND,
);
const graphqlError = catchGraphqlError(error);
expect(graphqlError.extensions.code).toBe(ErrorCode.NOT_FOUND);
expect(graphqlError.extensions.subCode).toBe(
AgentExceptionCode.AGENT_NOT_FOUND,
);
expect(graphqlError.extensions.userFriendlyMessage).toBeDefined();
});
});
@@ -58,7 +58,7 @@ export class AgentChatSubscriptionResolver {
if (!isDefined(thread)) {
throw new AgentException(
'Thread not found',
AgentExceptionCode.AGENT_EXECUTION_FAILED,
AgentExceptionCode.AGENT_NOT_FOUND,
);
}
@@ -169,7 +169,7 @@ export class AgentChatResolver {
if (!isDefined(thread)) {
throw new AgentException(
'Thread not found',
AgentExceptionCode.AGENT_EXECUTION_FAILED,
AgentExceptionCode.AGENT_NOT_FOUND,
);
}
@@ -247,7 +247,7 @@ export class AgentChatResolver {
if (!isDefined(message)) {
throw new AgentException(
'Queued message not found',
AgentExceptionCode.AGENT_EXECUTION_FAILED,
AgentExceptionCode.AGENT_NOT_FOUND,
);
}
@@ -258,7 +258,7 @@ export class AgentChatResolver {
if (!isDefined(thread)) {
throw new AgentException(
'Thread not found',
AgentExceptionCode.AGENT_EXECUTION_FAILED,
AgentExceptionCode.AGENT_NOT_FOUND,
);
}
@@ -79,7 +79,7 @@ export class AgentChatStreamingService {
if (!thread) {
throw new AgentException(
'Thread not found',
AgentExceptionCode.AGENT_EXECUTION_FAILED,
AgentExceptionCode.AGENT_NOT_FOUND,
);
}
@@ -97,7 +97,7 @@ export class AgentChatService {
if (!thread) {
throw new AgentException(
'Thread not found',
AgentExceptionCode.AGENT_EXECUTION_FAILED,
AgentExceptionCode.AGENT_NOT_FOUND,
);
}
@@ -180,7 +180,7 @@ export class AgentChatService {
if (!thread) {
throw new AgentException(
'Thread not found',
AgentExceptionCode.AGENT_EXECUTION_FAILED,
AgentExceptionCode.AGENT_NOT_FOUND,
);
}