vxcontrol/pentagi · error · Fatal

request is invalid

Error message

request is invalid

What it means

Thrown in tavily.parseHTTPResponse when the Tavily /search endpoint answers HTTP 400 Bad Request, mapped to the static message "request is invalid". Classified Fatal: the request body was rejected by the API as malformed or semantically invalid, so retrying the identical request will not help. The searcher deliberately discards Tavily's own error detail and returns this generic message.

Source

Thrown at backend/pkg/tools/searchers/tavily.go:150

	resp, err := client.Do(req)
	if err != nil {
		return "", Retryable(fmt.Errorf("failed to do request: %v", err), 0)
	}
	defer resp.Body.Close()

	return t.parseHTTPResponse(ctx, resp)
}

func (t *tavily) parseHTTPResponse(ctx context.Context, resp *http.Response) (string, error) {
	switch resp.StatusCode {
	case http.StatusOK:
		var respBody tavilySearchResult
		if err := json.NewDecoder(resp.Body).Decode(&respBody); err != nil {
			return "", Fatal(fmt.Errorf("failed to decode response body: %v", err))
		}
		return t.buildTavilyResult(ctx, &respBody), nil
	case http.StatusBadRequest:
		return "", Fatal(fmt.Errorf("request is invalid"))
	case http.StatusUnauthorized:
		return "", Fatal(fmt.Errorf("API key is wrong"))
	case http.StatusForbidden:
		return "", Fatal(fmt.Errorf("the endpoint requested is hidden for administrators only"))
	case http.StatusNotFound:
		return "", Fatal(fmt.Errorf("the specified endpoint could not be found"))
	case http.StatusMethodNotAllowed:
		return "", Fatal(fmt.Errorf("there need to try to access an endpoint with an invalid method"))
	case http.StatusTooManyRequests:
		return "", Retryable(fmt.Errorf("there are requesting too many results"), 0)
	case http.StatusInternalServerError:
		return "", Retryable(fmt.Errorf("there had a problem with our server. try again later"), 0)
	case http.StatusBadGateway:
		return "", Retryable(fmt.Errorf("there was a problem with the server. Please try again later"), 0)
	case http.StatusServiceUnavailable:
		return "", Retryable(fmt.Errorf("there are temporarily offline for maintenance. please try again later"), 0)
	case http.StatusGatewayTimeout:
		return "", Retryable(fmt.Errorf("there are temporarily offline for maintenance. please try again later"), 0)

View on GitHub (pinned to ea665308ba)

Solutions

  1. Validate the Request before calling Tavily: reject empty/overlong queries and clamp MaxResults to the API's allowed range.
  2. Log the outgoing request body (query, max_results, search_depth) when this fires to identify which parameter the API rejected.
  3. Compare against the current Tavily /search API docs — parameter constraints may have changed.
  4. Capture Tavily's 400 response body (it contains a detail message) instead of the static string for faster diagnosis.
  5. If an agent generates the query, add prompt/schema-side guardrails so blank or malformed queries never reach the tool.

Example fix

// before
if strings.TrimSpace(req.Query) == "" { return "", ErrNotConfigured } // too late / wrong place
// after: guard in Handle before building the payload
q := strings.TrimSpace(req.Query)
if q == "" { return "", Fatal(fmt.Errorf("query must not be empty")) }
max := req.MaxResults
if max <= 0 { max = 5 } else if max > 20 { max = 20 } // keep within Tavily limits
Defensive patterns

Strategy: validation

Validate before calling

q := strings.TrimSpace(req.Query)
if q == "" || len(q) > 400 {
    return "", Fatal(fmt.Errorf("query must be 1-400 chars"))
}
max := req.MaxResults
if max <= 0 { max = 5 } else if max > 20 { max = 20 }

Try / catch

if err != nil {
    if errors.Is(err, ErrFatalSearcher) && strings.Contains(err.Error(), "request is invalid") {
        // log the outgoing query/max_results, correct the input, do not blind-retry
    }
    return err
}

Prevention

When it happens

Trigger: POST to api.tavily.com/search returns 400: empty or whitespace-only query, max_results out of the API's allowed range, invalid search_depth/topic values, or a malformed JSON body.

Common situations: Agent calls web_search with an empty or garbage query that flows through unchanged to Tavily; max_results configured above Tavily's cap; a Tavily API update tightening parameter validation (e.g. search_depth allowed values) while the client still sends "advanced" defaults that become invalid on a plan tier.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/06fa6ba93d83781d. Report an issue: GitHub.