Skip to content

Commit 5c624cd

Browse files
wip on better error handling
1 parent 1d1205f commit 5c624cd

6 files changed

Lines changed: 62 additions & 31 deletions

File tree

packages/web/src/actions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ export const sew = async <T>(fn: () => Promise<T>): Promise<T | ServiceError> =>
4848
Sentry.captureException(e);
4949
logger.error(e);
5050

51+
if (e instanceof ServiceErrorException) {
52+
return e.serviceError;
53+
}
54+
5155
if (e instanceof Error) {
5256
return unexpectedError(e.message);
5357
}

packages/web/src/app/[domain]/components/searchBar/searchBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ export const SearchBar = ({
287287
indentWithTab={false}
288288
autoFocus={autoFocus ?? false}
289289
/>
290-
<div className="flex flex-row items-center gap-1">
290+
<div className="flex flex-row items-center gap-1 ml-1">
291291
<Tooltip>
292292
<TooltipTrigger asChild>
293293
<span>

packages/web/src/app/[domain]/search/components/searchResultsPage.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { CodePreviewPanel } from "./codePreviewPanel";
3333
import { FilterPanel } from "./filterPanel";
3434
import { useFilteredMatches } from "./filterPanel/useFilterMatches";
3535
import { SearchResultsPanel, SearchResultsPanelHandle } from "./searchResultsPanel";
36+
import { ServiceErrorException } from "@/lib/serviceError";
3637

3738
interface SearchResultsPageProps {
3839
searchQuery: string;
@@ -79,7 +80,7 @@ export const SearchResultsPage = ({
7980
useEffect(() => {
8081
if (error) {
8182
toast({
82-
description: `❌ Search failed. Reason: ${error.message}`,
83+
description: `❌ Search failed. Reason: ${error instanceof ServiceErrorException ? error.serviceError.message : error.message}`,
8384
});
8485
}
8586
}, [error, toast]);
@@ -184,7 +185,7 @@ export const SearchResultsPage = ({
184185
<div className="flex flex-col items-center justify-center h-full gap-2">
185186
<AlertTriangleIcon className="h-6 w-6" />
186187
<p className="font-semibold text-center">Failed to search</p>
187-
<p className="text-sm text-center">{error.message}</p>
188+
<p className="text-sm text-center">{error instanceof ServiceErrorException ? error.serviceError.message : error.message}</p>
188189
</div>
189190
) : (
190191
<PanelGroup

packages/web/src/app/[domain]/search/useStreamedSearch.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
'use client';
22

33
import { RepositoryInfo, SearchRequest, SearchResultFile, SearchStats, StreamedSearchResponse } from '@/features/search';
4-
import { useState, useCallback, useRef, useEffect } from 'react';
4+
import { ServiceErrorException } from '@/lib/serviceError';
5+
import { isServiceError } from '@/lib/utils';
56
import * as Sentry from '@sentry/nextjs';
7+
import { useCallback, useEffect, useRef, useState } from 'react';
68

79
interface CacheEntry {
810
files: SearchResultFile[];
@@ -132,6 +134,14 @@ export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegex
132134
});
133135

134136
if (!response.ok) {
137+
// Check if this is a service error response
138+
const contentType = response.headers.get('content-type');
139+
if (contentType?.includes('application/json')) {
140+
const errorData = await response.json();
141+
if (isServiceError(errorData)) {
142+
throw new ServiceErrorException(errorData);
143+
}
144+
}
135145
throw new Error(`HTTP error! status: ${response.status}`);
136146
}
137147

@@ -249,7 +259,7 @@ export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegex
249259
...prev,
250260
isStreaming: false,
251261
timeToSearchCompletionMs,
252-
error: error as Error,
262+
error: error instanceof Error ? error : null,
253263
}));
254264
}
255265
}

packages/web/src/features/search/parser.ts

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import {
2424
import { parser as _parser } from '@sourcebot/query-language';
2525
import { PrismaClient } from '@sourcebot/db';
2626
import { SINGLE_TENANT_ORG_ID } from '@/lib/constants';
27+
import { ServiceErrorException } from '@/lib/serviceError';
28+
import { StatusCodes } from 'http-status-codes';
29+
import { ErrorCode } from '@/lib/errorCodes';
2730

2831
// Configure the parser to throw errors when encountering invalid syntax.
2932
const parser = _parser.configure({
@@ -61,35 +64,47 @@ export const parseQuerySyntaxIntoIR = async ({
6164
},
6265
prisma: PrismaClient,
6366
}): Promise<QueryIR> => {
64-
// First parse the query into a Lezer tree.
65-
const tree = parser.parse(query);
66-
67-
// Then transform the tree into the intermediate representation.
68-
return transformTreeToIR({
69-
tree,
70-
input: query,
71-
isCaseSensitivityEnabled: options.isCaseSensitivityEnabled ?? false,
72-
isRegexEnabled: options.isRegexEnabled ?? false,
73-
onExpandSearchContext: async (contextName: string) => {
74-
const context = await prisma.searchContext.findUnique({
75-
where: {
76-
name_orgId: {
77-
name: contextName,
78-
orgId: SINGLE_TENANT_ORG_ID,
67+
68+
try {
69+
// First parse the query into a Lezer tree.
70+
const tree = parser.parse(query);
71+
72+
// Then transform the tree into the intermediate representation.
73+
return transformTreeToIR({
74+
tree,
75+
input: query,
76+
isCaseSensitivityEnabled: options.isCaseSensitivityEnabled ?? false,
77+
isRegexEnabled: options.isRegexEnabled ?? false,
78+
onExpandSearchContext: async (contextName: string) => {
79+
const context = await prisma.searchContext.findUnique({
80+
where: {
81+
name_orgId: {
82+
name: contextName,
83+
orgId: SINGLE_TENANT_ORG_ID,
84+
}
85+
},
86+
include: {
87+
repos: true,
7988
}
80-
},
81-
include: {
82-
repos: true,
83-
}
84-
});
89+
});
8590

86-
if (!context) {
87-
throw new Error(`Search context "${contextName}" not found`);
88-
}
91+
if (!context) {
92+
throw new Error(`Search context "${contextName}" not found`);
93+
}
8994

90-
return context.repos.map((repo) => repo.name);
91-
},
92-
});
95+
return context.repos.map((repo) => repo.name);
96+
},
97+
});
98+
} catch (error) {
99+
if (error instanceof SyntaxError) {
100+
throw new ServiceErrorException({
101+
statusCode: StatusCodes.BAD_REQUEST,
102+
errorCode: ErrorCode.FAILED_TO_PARSE_QUERY,
103+
message: `Failed to parse query "${query}" with message: ${error.message}`,
104+
});
105+
}
106+
throw error;
107+
}
93108
}
94109

95110
/**

packages/web/src/lib/errorCodes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ export enum ErrorCode {
3434
API_KEY_NOT_FOUND = 'API_KEY_NOT_FOUND',
3535
INVALID_API_KEY = 'INVALID_API_KEY',
3636
CHAT_IS_READONLY = 'CHAT_IS_READONLY',
37+
FAILED_TO_PARSE_QUERY = 'FAILED_TO_PARSE_QUERY',
3738
}

0 commit comments

Comments
 (0)