{"record":{"id":"a2b3bf1a484517f0","repo":"can1357/oh-my-pi","slug":"parallel-err-message-dynamic-thrown-as-searchpr","errorCode":null,"errorMessage":"parallel: err.message (dynamic; thrown as SearchProviderError(\"parallel\", err.message, err.statusCode))","messagePattern":"parallel: err\\.message \\(dynamic; thrown as SearchProviderError\\(\"parallel\", err\\.message, err\\.statusCode\\)\\)","errorType":"exception","errorClass":"SearchProviderError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/web/search/providers/parallel.ts","lineNumber":171,"sourceCode":"\t\t\t\tfetch: params.fetch,\n\t\t\t},\n\t\t\tauthStorage,\n\t\t\tsessionId,\n\t\t\tsourcePolicy,\n\t\t);\n\n\t\treturn {\n\t\t\tprovider: \"parallel\",\n\t\t\tsources: toSearchSources(result.sources, numResults),\n\t\t\trequestId: result.requestId,\n\t\t};\n\t} catch (err) {\n\t\tif (err instanceof ParallelApiError) {\n\t\t\tif (typeof err.statusCode === \"number\") {\n\t\t\t\tconst classified = classifyProviderHttpError(\"parallel\", err.statusCode, err.message);\n\t\t\t\tif (classified) throw classified;\n\t\t\t}\n\t\t\tthrow new SearchProviderError(\"parallel\", err.message, err.statusCode);\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nexport class ParallelProvider extends SearchProvider {\n\treadonly id = \"parallel\";\n\treadonly label = \"Parallel\";\n\n\tisAvailable(authStorage: AuthStorage) {\n\t\treturn !!getEnvApiKey(\"parallel\") || authStorage.hasAuth(\"parallel\");\n\t}\n\n\tsearch(params: SearchParams): Promise<SearchResponse> {\n\t\treturn searchParallel(\n\t\t\t{\n\t\t\t\tquery: params.query,\n\t\t\t\tnum_results: params.numSearchResults ?? params.limit,","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/web/search/providers/parallel.ts#L153-L189","documentation":"searchParallel catches a ParallelApiError from the Parallel web-search API, first offering it to a shared HTTP-error classifier (classifyProviderHttpError) and, if unclassified, re-wrapping the upstream message and status code in a SearchProviderError tagged with provider \"parallel\". This normalizes provider failures so callers of the aggregate search get one consistent error type regardless of which backend failed. The message is dynamic: it is whatever the Parallel API returned.","triggerScenarios":"Any HTTP error response from the Parallel Search API (searchParallel, invoked via search/result) whose status is not already mapped by classifyProviderHttpError — e.g. 401 invalid key, 429 rate limit, 5xx outage, or a malformed request the API rejects.","commonSituations":"Expired or missing PARALLEL_API_KEY, exceeded requests-per-minute quota, Parallel-side incidents, invalid query parameters (bad domain filter, oversized numResults).","solutions":["Check the err.statusCode and message to identify the upstream cause (auth vs rate limit vs server error)","If auth-related, verify the Parallel API key is set and valid","If 429, back off and retry after the rate-limit window","Check the Parallel status page if statusCode >= 500"],"exampleFix":"// before\nconst r = await search({ provider: \"parallel\", query: q });\n// after\ntry {\n  const r = await search({ provider: \"parallel\", query: q });\n} catch (err) {\n  if (err instanceof SearchProviderError && err.provider === \"parallel\") {\n    // inspect err.statusCode (401 -> fix key, 429 -> backoff)\n  }\n}","handlingStrategy":"try-catch","validationCode":"if (!process.env.PARALLEL_API_KEY) throw new Error('Parallel search requires PARALLEL_API_KEY');","typeGuard":"function isSearchProviderError(e: unknown): e is SearchProviderError {\n  return e instanceof SearchProviderError;\n}","tryCatchPattern":"try {\n  const res = await search({ provider: \"parallel\", query });\n} catch (err) {\n  if (err instanceof SearchProviderError && err.provider === \"parallel\") {\n    if (err.statusCode === 429) await backoffAndRetry();\n    else if (err.statusCode === 401) fixCredentials();\n    else fallbackProvider();\n  } else throw err;\n}","preventionTips":["Validate the Parallel API key at startup","Respect rate limits with client-side throttling","Configure a fallback search provider for the query"],"tags":["network","http","web-search","provider-error"],"backgroundTag":"search-provider-http-error","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}