Skip to content

Commit c85ea6b

Browse files
Improve error message extraction from Mapbox API responses (#102)
* Improve error message extraction from Mapbox API responses Add getErrorMessage() helper method to MapboxApiBasedTool base class that extracts the 'message' field from JSON error responses when available, falling back to status text otherwise. Updated all 10 API-based tools to use this helper: - DirectionsTool - SearchAndGeocodeTool - OptimizationTool - MapMatchingTool - MatrixTool - ReverseGeocodeTool - CategorySearchTool - StaticMapImageTool - IsochroneTool - CategoryListTool Benefits: - More informative error messages (e.g., "Not Authorized - Invalid Token" vs "Unauthorized") - Consistent error handling across all tools - Centralized error parsing logic - Simplified code (reduced from 20+ lines to 5 lines in some tools) Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * Include HTTP status code in error messages --------- Co-authored-by: Claude Sonnet 4.5 <[email protected]>
1 parent 036bd3e commit c85ea6b

File tree

18 files changed

+58
-46
lines changed

18 files changed

+58
-46
lines changed

src/tools/MapboxApiBasedTool.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,26 @@ export abstract class MapboxApiBasedTool<
5555
return parts.every((part) => part.length > 0);
5656
}
5757

58+
/**
59+
* Extracts error message from Mapbox API response.
60+
* If the response contains a JSON body with a 'message' field, returns that.
61+
* Otherwise falls back to the response status text.
62+
* @param response The HTTP response object
63+
* @returns The error message string in format "statusCode: message"
64+
*/
65+
protected async getErrorMessage(response: Response): Promise<string> {
66+
try {
67+
const errorBody = await response.text();
68+
const errorJson = JSON.parse(errorBody);
69+
if (errorJson.message) {
70+
return `${response.status}: ${errorJson.message}`;
71+
}
72+
} catch {
73+
// If parsing fails, fall back to status text
74+
}
75+
return `${response.status}: ${response.statusText}`;
76+
}
77+
5878
/**
5979
* Validates and runs the tool logic.
6080
*/

src/tools/category-list-tool/CategoryListTool.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,12 @@ export class CategoryListTool extends MapboxApiBasedTool<
7171
});
7272

7373
if (!response.ok) {
74+
const errorMessage = await this.getErrorMessage(response);
7475
return {
7576
content: [
7677
{
7778
type: 'text',
78-
text: `Mapbox API request failed: ${response.status} ${response.statusText}`
79+
text: `Category List API error: ${errorMessage}`
7980
}
8081
],
8182
isError: true

src/tools/category-search-tool/CategorySearchTool.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,12 @@ export class CategorySearchTool extends MapboxApiBasedTool<
149149
const response = await this.httpRequest(url.toString());
150150

151151
if (!response.ok) {
152+
const errorMessage = await this.getErrorMessage(response);
152153
return {
153154
content: [
154155
{
155156
type: 'text',
156-
text: `Failed to search category: ${response.status} ${response.statusText}`
157+
text: `Category Search API error: ${errorMessage}`
157158
}
158159
],
159160
isError: true

src/tools/directions-tool/DirectionsTool.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,11 +241,12 @@ export class DirectionsTool extends MapboxApiBasedTool<
241241
const response = await this.httpRequest(url);
242242

243243
if (!response.ok) {
244+
const errorMessage = await this.getErrorMessage(response);
244245
return {
245246
content: [
246247
{
247248
type: 'text',
248-
text: `Request failed with status ${response.status}: ${response.statusText}`
249+
text: `Directions API error: ${errorMessage}`
249250
}
250251
],
251252
isError: true

src/tools/isochrone-tool/IsochroneTool.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,12 @@ export class IsochroneTool extends MapboxApiBasedTool<
136136
const response = await this.httpRequest(url);
137137

138138
if (!response.ok) {
139+
const errorMessage = await this.getErrorMessage(response);
139140
return {
140141
content: [
141142
{
142143
type: 'text',
143-
text: `Failed to calculate isochrones: ${response.status} ${response.statusText}`
144+
text: `Isochrone API error: ${errorMessage}`
144145
}
145146
],
146147
isError: true

src/tools/map-matching-tool/MapMatchingTool.ts

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -105,22 +105,14 @@ export class MapMatchingTool extends MapboxApiBasedTool<
105105
const response = await this.httpRequest(url);
106106

107107
if (!response.ok) {
108-
const errorText = await response.text();
109-
let errorMessage = `Request failed with status ${response.status}: ${response.statusText}`;
110-
111-
try {
112-
const errorJson = JSON.parse(errorText);
113-
if (errorJson.message) {
114-
errorMessage = `${errorMessage} - ${errorJson.message}`;
115-
}
116-
} catch {
117-
if (errorText) {
118-
errorMessage = `${errorMessage} - ${errorText}`;
119-
}
120-
}
121-
108+
const errorMessage = await this.getErrorMessage(response);
122109
return {
123-
content: [{ type: 'text', text: errorMessage }],
110+
content: [
111+
{
112+
type: 'text',
113+
text: `Map Matching API error: ${errorMessage}`
114+
}
115+
],
124116
isError: true
125117
};
126118
}

src/tools/matrix-tool/MatrixTool.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,11 +282,12 @@ export class MatrixTool extends MapboxApiBasedTool<
282282
const response = await this.httpRequest(url);
283283

284284
if (!response.ok) {
285+
const errorMessage = await this.getErrorMessage(response);
285286
return {
286287
content: [
287288
{
288289
type: 'text',
289-
text: `Request failed with status ${response.status}: ${response.statusText}`
290+
text: `Matrix API error: ${errorMessage}`
290291
}
291292
],
292293
isError: true

src/tools/optimization-tool/OptimizationTool.ts

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -101,27 +101,20 @@ export class OptimizationTool extends MapboxApiBasedTool<
101101
const response = await this.httpRequest(url);
102102

103103
if (!response.ok) {
104-
const errorText = await response.text();
105-
let errorMessage = `Request failed with status ${response.status}: ${response.statusText}`;
106-
107-
try {
108-
const errorJson = JSON.parse(errorText);
109-
if (errorJson.message) {
110-
errorMessage = `${errorMessage} - ${errorJson.message}`;
111-
}
112-
} catch {
113-
if (errorText) {
114-
errorMessage = `${errorMessage} - ${errorText}`;
115-
}
116-
}
104+
const errorMessage = await this.getErrorMessage(response);
117105

118106
toolContext.span.setStatus({
119107
code: SpanStatusCode.ERROR,
120108
message: errorMessage
121109
});
122110

123111
return {
124-
content: [{ type: 'text' as const, text: errorMessage }],
112+
content: [
113+
{
114+
type: 'text' as const,
115+
text: `Optimization API error: ${errorMessage}`
116+
}
117+
],
125118
isError: true
126119
};
127120
}

src/tools/reverse-geocode-tool/ReverseGeocodeTool.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,12 @@ export class ReverseGeocodeTool extends MapboxApiBasedTool<
136136
const response = await this.httpRequest(url.toString());
137137

138138
if (!response.ok) {
139+
const errorMessage = await this.getErrorMessage(response);
139140
return {
140141
content: [
141142
{
142143
type: 'text',
143-
text: `Failed to reverse geocode: ${response.status} ${response.statusText}`
144+
text: `Reverse Geocode API error: ${errorMessage}`
144145
}
145146
],
146147
isError: true

src/tools/search-and-geocode-tool/SearchAndGeocodeTool.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,16 @@ export class SearchAndGeocodeTool extends MapboxApiBasedTool<
175175
const response = await this.httpRequest(url.toString());
176176

177177
if (!response.ok) {
178-
const errorBody = await response.text();
178+
const errorMessage = await this.getErrorMessage(response);
179179
this.log(
180180
'error',
181-
`SearchAndGeocodeTool: API Error - Status: ${response.status}, Body: ${errorBody}`
181+
`SearchAndGeocodeTool: API Error - Status: ${response.status}, Message: ${errorMessage}`
182182
);
183183
return {
184184
content: [
185185
{
186186
type: 'text',
187-
text: `Failed to search: ${response.status} ${response.statusText}`
187+
text: `Search API error: ${errorMessage}`
188188
}
189189
],
190190
isError: true

0 commit comments

Comments
 (0)