Skip to content

Commit ec62ab4

Browse files
committed
fix: add comprehensive error handling for JSON parsing failures
- Add url field to ApiError class for better debugging context - Add try/catch blocks around all response.json() calls in success paths - Include original error messages in JSON parsing error details - Update ApiErrorType to include optional url field Previously, if a server returned a successful status (200) but invalid JSON (e.g., HTML error pages, malformed responses), the hooks would throw generic parse errors. Now they throw structured ApiError instances with context about what went wrong and which URL failed.
1 parent 6b75fcc commit ec62ab4

File tree

7 files changed

+56
-10
lines changed

7 files changed

+56
-10
lines changed

src/hooks/useCollection.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,18 @@ function useCollection(collectionId: string): StacCollectionHook {
2727
detail = await response.text();
2828
}
2929

30-
throw new ApiError(response.statusText, response.status, detail);
30+
throw new ApiError(response.statusText, response.status, detail, response.url);
31+
}
32+
try {
33+
return await response.json();
34+
} catch (error) {
35+
throw new ApiError(
36+
'Invalid JSON Response',
37+
response.status,
38+
`Response is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
39+
response.url
40+
);
3141
}
32-
return await response.json();
3342
};
3443

3544
const {

src/hooks/useCollections.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,18 @@ function useCollections(): StacCollectionsHook {
2727
detail = await response.text();
2828
}
2929

30-
throw new ApiError(response.statusText, response.status, detail);
30+
throw new ApiError(response.statusText, response.status, detail, response.url);
31+
}
32+
try {
33+
return await response.json();
34+
} catch (error) {
35+
throw new ApiError(
36+
'Invalid JSON Response',
37+
response.status,
38+
`Response is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
39+
response.url
40+
);
3141
}
32-
return await response.json();
3342
};
3443

3544
const {

src/hooks/useItem.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,18 @@ function useItem(url: string): ItemHook {
2727
detail = await response.text();
2828
}
2929

30-
throw new ApiError(response.statusText, response.status, detail);
30+
throw new ApiError(response.statusText, response.status, detail, response.url);
31+
}
32+
try {
33+
return await response.json();
34+
} catch (error) {
35+
throw new ApiError(
36+
'Invalid JSON Response',
37+
response.status,
38+
`Response is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
39+
response.url
40+
);
3141
}
32-
return await response.json();
3342
};
3443

3544
const {

src/hooks/useStacApi.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@ function useStacApi(url: string, options?: GenericObject): StacApiHook {
2222
},
2323
});
2424
const baseUrl = response.url;
25-
const json = await response.json();
25+
let json;
26+
try {
27+
json = await response.json();
28+
} catch (error) {
29+
throw new Error(
30+
`Invalid JSON response from STAC API: ${error instanceof Error ? error.message : String(error)}`
31+
);
32+
}
2633
const doesPost = json.links?.find(
2734
({ rel, method }: Link) => rel === 'search' && method === 'POST'
2835
);

src/hooks/useStacSearch.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,18 @@ function useStacSearch(): StacSearchHook {
125125
detail = await response.text();
126126
}
127127

128-
throw new ApiError(response.statusText, response.status, detail);
128+
throw new ApiError(response.statusText, response.status, detail, response.url);
129+
}
130+
try {
131+
return await response.json();
132+
} catch (error) {
133+
throw new ApiError(
134+
'Invalid JSON Response',
135+
response.status,
136+
`Response is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
137+
response.url
138+
);
129139
}
130-
return await response.json();
131140
};
132141

133142
/**

src/types/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export type ApiErrorType = {
66
detail?: GenericObject | string;
77
status: number;
88
statusText: string;
9+
url?: string;
910
};

src/utils/ApiError.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ export class ApiError extends Error {
88
status: number;
99
statusText: string;
1010
detail?: GenericObject | string;
11+
url?: string;
1112

12-
constructor(statusText: string, status: number, detail?: GenericObject | string) {
13+
constructor(statusText: string, status: number, detail?: GenericObject | string, url?: string) {
1314
super(statusText);
1415
this.name = 'ApiError';
1516
this.status = status;
1617
this.statusText = statusText;
1718
this.detail = detail;
19+
this.url = url;
1820

1921
// Maintains proper stack trace for where our error was thrown
2022
// Note: Error.captureStackTrace is a V8-only feature (Node.js, Chrome)

0 commit comments

Comments
 (0)