{"record":{"id":"631ec6c92901a97c","repo":"can1357/oh-my-pi","slug":"provider-label-returned-no-renderable-search-co","errorCode":null,"errorMessage":"${provider.label} returned no renderable search content.","messagePattern":"(.+?) returned no renderable search content\\.","errorType":"exception","errorClass":"SearchProviderError","httpStatus":204,"severity":"warning","filePath":"packages/coding-agent/src/web/search/index.ts","lineNumber":240,"sourceCode":"\n\t\t\t// Lenient constraint pass over whatever the provider returned: enforce\n\t\t\t// site:/inurl:/intitle:/filetype:/date directives the provider could\n\t\t\t// not (or only partially) honor natively, relaxing any dimension that\n\t\t\t// would wipe out every result. Citations/answer text stay untouched.\n\t\t\tlet finalResponse = response;\n\t\t\tconst constraintNotes: string[] = [];\n\t\t\tif (parsedQuery.hasConstraints && response.sources.length > 0) {\n\t\t\t\tconst filtered = applyQueryConstraints(response.sources, parsedQuery);\n\t\t\t\tif (filtered.sources.length !== response.sources.length) {\n\t\t\t\t\tfinalResponse = { ...response, sources: filtered.sources };\n\t\t\t\t}\n\t\t\t\tfor (const label of filtered.dropped) {\n\t\t\t\t\tconstraintNotes.push(`no results matched \\`${label}\\`; the constraint was relaxed`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!hasRenderableSearchContent(finalResponse)) {\n\t\t\t\tthrow new SearchProviderError(provider.id, `${provider.label} returned no renderable search content.`, 204);\n\t\t\t}\n\n\t\t\tconst text = formatForLLM(finalResponse, constraintNotes);\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { response: finalResponse },\n\t\t\t};\n\t\t} catch (error) {\n\t\t\t// Surface user-initiated cancellation immediately so the session sees\n\t\t\t// a clean abort instead of a generic \"all providers failed\" message.\n\t\t\t// Without this, an AbortError from `fetch()` is treated as a provider\n\t\t\t// failure and the loop falls through to the next provider (or to the\n\t\t\t// summary error), masking the cancellation.\n\t\t\tthrowIfAborted(signal);\n\t\t\tfailures.push({ provider: provider ?? providerMeta, error });\n\t\t}\n\t}","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/web/search/index.ts#L222-L258","documentation":"After a provider responds, executeSearch validates that the response actually contains renderable content (results/text). If hasRenderableSearchContent returns false it throws SearchProviderError with status 204, meaning the provider answered but nothing usable came back — an empty or unusable payload, not a transport error.","triggerScenarios":"A search provider returns HTTP 200 with an empty result set, only filtered-out results, or a payload shape that renders to nothing (e.g. all results dropped by domain/exclude constraints).","commonSituations":"Very niche query with zero hits; overly restrictive include/exclude filters dropping every result; provider API change degrading response shape; region/language restrictions returning empty results.","solutions":["Broaden the query (fewer/shorter terms, remove site: filters).","Relax include/exclude domain constraints that filtered out all results.","Retry with the next provider in the chain instead of pinning one provider.","Catch SearchProviderError with status 204 and fall back to another provider or report 'no results' to the model/user."],"exampleFix":"// before\nconst res = await runSearchQuery({ query, provider: \"brave\", excludeDomains: many }, opts);\n// after\ntry {\n  return await runSearchQuery({ query, provider: \"brave\" }, opts);\n} catch (e) {\n  if (e instanceof SearchProviderError && e.status === 204) {\n    return await runSearchQuery({ query }, opts); // automatic chain fallback\n  }\n  throw e;\n}","handlingStrategy":"fallback","validationCode":"// After receiving a response, before trusting it:\nif (!hasRenderableSearchContent(response)) return null; // trigger fallback","typeGuard":"null","tryCatchPattern":"try { return await executeSearchWith(providerId, params); }\ncatch (e) {\n  if (e instanceof SearchProviderError && e.status === 204) {\n    return await runSearchQuery(params, opts); // try next provider in chain\n  }\n  throw e;\n}","preventionTips":["Relax include/exclude filters when they can plausibly drop all results.","Don't hard-pin a single provider; allow chain fallback.","Detect empty-result queries up front and set expectations (report 'no results' vs provider failure).","Log the provider response summary when this fires to catch provider-side shape changes early."],"tags":["search-provider","empty-results","response-validation"],"backgroundTag":"empty-response-payload","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}