can1357/oh-my-pi · warning · SearchProviderError

${provider.label} returned no renderable search content.

Error message

${provider.label} returned no renderable search content.

What it means

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.

Source

Thrown at packages/coding-agent/src/web/search/index.ts:240

			// Lenient constraint pass over whatever the provider returned: enforce
			// site:/inurl:/intitle:/filetype:/date directives the provider could
			// not (or only partially) honor natively, relaxing any dimension that
			// would wipe out every result. Citations/answer text stay untouched.
			let finalResponse = response;
			const constraintNotes: string[] = [];
			if (parsedQuery.hasConstraints && response.sources.length > 0) {
				const filtered = applyQueryConstraints(response.sources, parsedQuery);
				if (filtered.sources.length !== response.sources.length) {
					finalResponse = { ...response, sources: filtered.sources };
				}
				for (const label of filtered.dropped) {
					constraintNotes.push(`no results matched \`${label}\`; the constraint was relaxed`);
				}
			}

			if (!hasRenderableSearchContent(finalResponse)) {
				throw new SearchProviderError(provider.id, `${provider.label} returned no renderable search content.`, 204);
			}

			const text = formatForLLM(finalResponse, constraintNotes);

			return {
				content: [{ type: "text" as const, text }],
				details: { response: finalResponse },
			};
		} catch (error) {
			// Surface user-initiated cancellation immediately so the session sees
			// a clean abort instead of a generic "all providers failed" message.
			// Without this, an AbortError from `fetch()` is treated as a provider
			// failure and the loop falls through to the next provider (or to the
			// summary error), masking the cancellation.
			throwIfAborted(signal);
			failures.push({ provider: provider ?? providerMeta, error });
		}
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Broaden the query (fewer/shorter terms, remove site: filters).
  2. Relax include/exclude domain constraints that filtered out all results.
  3. Retry with the next provider in the chain instead of pinning one provider.
  4. Catch SearchProviderError with status 204 and fall back to another provider or report 'no results' to the model/user.

Example fix

// before
const res = await runSearchQuery({ query, provider: "brave", excludeDomains: many }, opts);
// after
try {
  return await runSearchQuery({ query, provider: "brave" }, opts);
} catch (e) {
  if (e instanceof SearchProviderError && e.status === 204) {
    return await runSearchQuery({ query }, opts); // automatic chain fallback
  }
  throw e;
}
Defensive patterns

Strategy: fallback

Validate before calling

// After receiving a response, before trusting it:
if (!hasRenderableSearchContent(response)) return null; // trigger fallback

Type guard

null

Try / catch

try { return await executeSearchWith(providerId, params); }
catch (e) {
  if (e instanceof SearchProviderError && e.status === 204) {
    return await runSearchQuery(params, opts); // try next provider in chain
  }
  throw e;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/631ec6c92901a97c. Report an issue: GitHub.