{"record":{"id":"9c9c4e573f40289a","repo":"jackwener/OpenCLI","slug":"trip-com-poisearch-fetch-failed-err-instanceof","errorCode":null,"errorMessage":"Trip.com poiSearch fetch failed: ${err instanceof Error ? err.message : String(err)}","messagePattern":"Trip\\.com poiSearch fetch failed: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/trip/utils.js","lineNumber":842,"sourceCode":" * airport / place matches, so this needs no browser session. Returns the raw\n * `results` array. Missing/non-array `results` means schema drift; an explicit\n * empty array is the only valid empty-result shape.\n */\nexport async function fetchPoiSearch(keyword) {\n    let response;\n    try {\n        response = await fetch(POI_SEARCH_ENDPOINT, {\n            method: 'POST',\n            headers: { 'content-type': 'application/json', currency: 'USD' },\n            body: JSON.stringify({\n                key: keyword,\n                mode: '0',\n                tripType: 'RT',\n                Head: { Currency: 'USD', Locale: 'en-US', Source: 'ONLINE', Channel: 'EnglishSite', ClientID: 'opencli-trip' },\n            }),\n        });\n    } catch (err) {\n        throw new CommandExecutionError(`Trip.com poiSearch fetch failed: ${err instanceof Error ? err.message : String(err)}`);\n    }\n    if (!response.ok) {\n        throw new CommandExecutionError(`Trip.com poiSearch failed with status ${response.status}`);\n    }\n    let payload;\n    try {\n        payload = await response.json();\n    } catch (err) {\n        throw new CommandExecutionError(`Trip.com poiSearch returned invalid JSON: ${err instanceof Error ? err.message : String(err)}`);\n    }\n    if (!Array.isArray(payload?.results)) {\n        throw new CommandExecutionError('Trip.com poiSearch returned malformed payload: missing results array');\n    }\n    return payload.results;\n}\n\n/**\n * Flatten POI results into a flat suggestion list: each top-level city keeps its","sourceCodeStart":824,"sourceCodeEnd":860,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/trip/utils.js#L824-L860","documentation":"fetchPoiSearch wraps the underlying fetch() to the Trip.com poiSearch endpoint. If fetch itself throws — network unreachable, DNS failure, TLS error, connection reset, timeout — the error is re-thrown as CommandExecutionError prefixed with 'Trip.com poiSearch fetch failed:'. It is distinct from HTTP status errors, which are reported separately.","triggerScenarios":"Calling any command path that goes through results -> fetchPoiSearch while offline; DNS cannot resolve the Trip.com API host; a proxy/firewall blocks the request; Node lacks a trusted CA for the TLS handshake; transient connection reset mid-handshake.","commonSituations":"Corporate proxy intercepting HTTPS; VPN dropping mid-request; missing NODE_EXTRA_CA_CERTS on corporate networks; IPv6 misconfiguration; rate-limit-induced connection drops at the network layer.","solutions":["Check network connectivity and DNS for the Trip.com API host (curl -v the endpoint)","Retry — the error is often transient; add backoff around the CLI invocation","If behind a proxy, set HTTPS_PROXY and ensure the proxy CA is trusted (NODE_EXTRA_CA_CERTS)","Read the inner err.message in the thrown text to identify the exact cause (ENOTFOUND, ECONNRESET, certificate, etc.)"],"exampleFix":"// before\nawait tripcli(['attractions-search','--query','Kyoto'])  // offline laptop\n// after\n// connect/VPN first, then retry with backoff\nawait retry(() => tripcli(['attractions-search','--query','Kyoto']), {retries:3, backoff:1000})","handlingStrategy":"try-catch","validationCode":"// pre-check connectivity before invoking\ntry { await fetch('https://www.trip.com', { method: 'HEAD' }); } catch (e) { throw new Error('network unavailable: ' + e.message); }","typeGuard":"null","tryCatchPattern":"try {\n  const results = await searchAttractions(keyword);\n} catch (e) {\n  if (/poiSearch fetch failed:/.test(e.message)) {\n    if (/ENOTFOUND|ECONNRESET|ETIMEDOUT/.test(e.message)) return retryWithBackoff(() => searchAttractions(keyword), 3);\n    if (/certificate|CERT/.test(e.message)) { process.env.NODE_EXTRA_CA_CERTS && console.error('check CA bundle'); }\n  }\n  throw e;\n}","preventionTips":["Retry transient network errors with exponential backoff","Set HTTPS_PROXY/NODE_EXTRA_CA_CERTS correctly on corporate networks","Monitor connectivity before batch runs","Read the appended inner error message to distinguish DNS vs TLS vs reset"],"tags":["network","fetch","http"],"backgroundTag":"fetch-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}