Skip to content

Commit 84a4595

Browse files
cablateclaude
andauthored
fix: improve transit error messages for unsupported regions (#75)
* fix: improve transit error messages for unsupported regions (#74) The Google Routes API does not support transit directions in some regions (notably Japan and India). Previously, users received a generic "No route found" error. Now the error message clearly explains the regional limitation and suggests alternatives. - computeRoutes: detect transit mode + empty routes → descriptive error - computeRouteMatrix: track ROUTE_NOT_FOUND count, throw on all-fail, attach warning on partial-fail for transit mode Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add transit error message tests for unsupported regions Adds Test 8 to smoke tests verifying: - Transit directions in Japan returns isError with descriptive message - Transit distance matrix in Japan returns descriptive error - Driving directions baseline still works (graceful on transient API issues) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: avoid passing default departureTime to Routes API PlacesSearcher.getDirections defaulted to `new Date()` when no departure_time was provided, but Routes API rejects past timestamps causing sporadic "Timestamp must be set to a future time" errors. Now omits departureTime entirely when not explicitly provided, letting the Routes API use its own default behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add departureTime fix to CHANGELOG Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: format RoutesService and PlacesSearcher with Prettier Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f31dbc6 commit 84a4595

4 files changed

Lines changed: 130 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 0.0.51
4+
5+
- fix: improve transit error messages for unsupported regions (Japan, India) (#74)
6+
- fix: avoid passing default departureTime to Routes API (sporadic "Timestamp must be set to a future time" errors)
7+
38
## 0.0.50
49

510
- docs: add CODE_OF_CONDUCT.md

src/services/PlacesSearcher.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -328,14 +328,14 @@ export class PlacesSearcher {
328328
arrival_time?: string
329329
): Promise<DirectionsResponse> {
330330
try {
331-
const departureTime = departure_time ? new Date(departure_time) : new Date();
331+
const departureTime = departure_time ? new Date(departure_time) : undefined;
332332
const arrivalTime = arrival_time ? new Date(arrival_time) : undefined;
333333
const result = await this.routesService.computeRoutes({
334334
origin,
335335
destination,
336336
mode,
337-
departureTime,
338-
arrivalTime,
337+
...(departureTime ? { departureTime } : {}),
338+
...(arrivalTime ? { arrivalTime } : {}),
339339
});
340340

341341
return {

src/services/RoutesService.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,15 @@ export class RoutesService {
176176
const data = await response.json();
177177

178178
if (!data.routes || data.routes.length === 0) {
179-
throw new Error(
180-
`No route found from "${params.origin}" to "${params.destination}" with mode: ${params.mode || "driving"}`
181-
);
179+
const mode = params.mode || "driving";
180+
if (mode === "transit") {
181+
throw new Error(
182+
`No transit route found from "${params.origin}" to "${params.destination}". ` +
183+
`The Google Routes API does not support transit directions in some regions (notably Japan and India). ` +
184+
`Try using mode "driving" or "walking" instead, or use a regional transit service for public transportation details.`
185+
);
186+
}
187+
throw new Error(`No route found from "${params.origin}" to "${params.destination}" with mode: ${mode}`);
182188
}
183189

184190
const route = data.routes[0];
@@ -218,6 +224,7 @@ export class RoutesService {
218224
durations: any[][];
219225
origin_addresses: string[];
220226
destination_addresses: string[];
227+
warning?: string;
221228
}> {
222229
const travelMode = TRAVEL_MODE_MAP[params.mode || "driving"] || "DRIVE";
223230

@@ -260,11 +267,15 @@ export class RoutesService {
260267
const distances: any[][] = Array.from({ length: rowCount }, () => Array(colCount).fill(null));
261268
const durations: any[][] = Array.from({ length: rowCount }, () => Array(colCount).fill(null));
262269

270+
let routeNotFoundCount = 0;
263271
for (const element of elements) {
264272
const i = element.originIndex;
265273
const j = element.destinationIndex;
266274
if (i === undefined || j === undefined) continue;
267-
if (element.condition === "ROUTE_NOT_FOUND") continue;
275+
if (element.condition === "ROUTE_NOT_FOUND") {
276+
routeNotFoundCount++;
277+
continue;
278+
}
268279

269280
const distMeters = element.distanceMeters || 0;
270281
const durSeconds = parseDuration(element.duration);
@@ -279,12 +290,28 @@ export class RoutesService {
279290
};
280291
}
281292

293+
const totalPairs = rowCount * colCount;
294+
295+
// All pairs failed — likely a regional transit limitation
296+
if (routeNotFoundCount === totalPairs && travelMode === "TRANSIT") {
297+
throw new Error(
298+
`No transit routes found for any origin/destination pair. ` +
299+
`The Google Routes API does not support transit directions in some regions (notably Japan and India). ` +
300+
`Try using mode "driving" or "walking" instead, or use a regional transit service for public transportation details.`
301+
);
302+
}
303+
282304
// Routes API doesn't return resolved addresses; use input strings
283305
return {
284306
distances,
285307
durations,
286308
origin_addresses: params.origins,
287309
destination_addresses: params.destinations,
310+
...(routeNotFoundCount > 0 && travelMode === "TRANSIT"
311+
? {
312+
warning: `${routeNotFoundCount} of ${totalPairs} origin/destination pairs returned no transit route. The Google Routes API has limited transit coverage in some regions.`,
313+
}
314+
: {}),
288315
};
289316
}
290317
}

tests/smoke.test.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,96 @@ async function testExecMode(): Promise<void> {
971971
}
972972
}
973973

974+
// --------------- Test 8: Transit Error Messages ---------------
975+
976+
async function testTransitErrorMessages(session: McpSession): Promise<void> {
977+
console.log("\n🧪 Test 8: Transit error messages for unsupported regions");
978+
979+
if (!API_KEY) {
980+
console.log(" ⏭️ Skipped (no GOOGLE_MAPS_API_KEY)");
981+
return;
982+
}
983+
984+
// Verify driving mode works fine first (baseline)
985+
const driveResult = await sendRequest(session, "tools/call", {
986+
name: "maps_directions",
987+
arguments: { origin: "Tokyo Station", destination: "Nagoya Station", mode: "driving" },
988+
});
989+
const driveContent = driveResult?.result?.content ?? [];
990+
assert(driveContent.length > 0, "Driving directions returns content");
991+
if (driveContent.length > 0) {
992+
const text = driveContent[0]?.text ?? "";
993+
const isError = driveResult?.result?.isError === true;
994+
assert(!isError, "Driving directions in Japan works (no error)", isError ? text.slice(0, 150) : undefined);
995+
if (!isError) {
996+
try {
997+
const parsed = JSON.parse(text);
998+
assert(parsed?.total_distance !== undefined, "Driving returns total_distance");
999+
assert(parsed?.total_duration !== undefined, "Driving returns total_duration");
1000+
} catch {
1001+
assert(false, "Driving directions returns valid JSON");
1002+
}
1003+
}
1004+
}
1005+
1006+
// Test directions with transit in Japan — should return improved error message
1007+
const dirResult = await sendRequest(session, "tools/call", {
1008+
name: "maps_directions",
1009+
arguments: { origin: "Tokyo Station", destination: "Nagoya Station", mode: "transit" },
1010+
});
1011+
const dirContent = dirResult?.result?.content ?? [];
1012+
assert(dirContent.length > 0, "Transit directions returns content");
1013+
if (dirContent.length > 0) {
1014+
const text = dirContent[0]?.text ?? "";
1015+
const isError = dirResult?.result?.isError === true;
1016+
assert(isError, "Transit directions in Japan returns isError=true");
1017+
assert(
1018+
text.includes("does not support transit") || text.includes("transit route"),
1019+
"Error message mentions transit limitation",
1020+
`got: ${text.slice(0, 200)}`
1021+
);
1022+
assert(
1023+
text.includes("Japan") || text.includes("region"),
1024+
"Error message mentions affected region",
1025+
`got: ${text.slice(0, 200)}`
1026+
);
1027+
}
1028+
1029+
// Test distance matrix with transit in Japan
1030+
const dmResult = await sendRequest(session, "tools/call", {
1031+
name: "maps_distance_matrix",
1032+
arguments: { origins: ["Tokyo Station"], destinations: ["Nagoya Station"], mode: "transit" },
1033+
});
1034+
const dmContent = dmResult?.result?.content ?? [];
1035+
assert(dmContent.length > 0, "Transit distance matrix returns content");
1036+
if (dmContent.length > 0) {
1037+
const text = dmContent[0]?.text ?? "";
1038+
const isError = dmResult?.result?.isError === true;
1039+
// May return error (all-fail) or warning (partial-fail)
1040+
if (isError) {
1041+
assert(
1042+
text.includes("does not support transit") || text.includes("transit route"),
1043+
"Distance matrix error mentions transit limitation",
1044+
`got: ${text.slice(0, 200)}`
1045+
);
1046+
} else {
1047+
// Partial success — check for warning in response
1048+
try {
1049+
const parsed = JSON.parse(text);
1050+
const hasWarning = parsed?.warning !== undefined;
1051+
const hasNulls = parsed?.distances?.[0]?.[0] === null;
1052+
assert(
1053+
hasWarning || hasNulls,
1054+
"Distance matrix returns warning or null entries for unsupported transit",
1055+
`warning=${hasWarning}, nulls=${hasNulls}`
1056+
);
1057+
} catch {
1058+
assert(false, "Distance matrix returns valid JSON", text.slice(0, 200));
1059+
}
1060+
}
1061+
}
1062+
}
1063+
9741064
// --------------- Main ---------------
9751065

9761066
async function main() {
@@ -994,6 +1084,7 @@ async function main() {
9941084
await testGeocode(session);
9951085
await testToolCalls(session);
9961086
await testPlaceDetailsPhotos(session);
1087+
await testTransitErrorMessages(session);
9971088
await testMultiSession();
9981089
} catch (err) {
9991090
console.error("\n💥 Fatal error:", err);

0 commit comments

Comments
 (0)