googleapis/mcp-toolbox · error

error reading start of json array: %w

Error message

error reading start of json array: %w

What it means

The tool streams the API response as a JSON array and reads its opening '[' token with json.Decoder.Token(). This error wraps any non-EOF failure while parsing that first token, meaning the response body is not the expected JSON array. Empty bodies (io.EOF) are explicitly treated as valid and return nil, so this always indicates malformed or unexpected content.

Source

Thrown at internal/tools/looker/lookerconversationalanalytics/lookerconversationalanalytics.go:458

	if err != nil {
		return nil, fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("API returned non-200 status: %d %s", resp.StatusCode, string(body))
	}

	var messages []map[string]any
	decoder := json.NewDecoder(resp.Body)

	// The response is a JSON array, so we read the opening bracket.
	if _, err := decoder.Token(); err != nil {
		if err == io.EOF {
			return nil, nil // Empty response is valid
		}
		return nil, fmt.Errorf("error reading start of json array: %w", err)
	}

	for decoder.More() {
		var msg StreamMessage
		if err := decoder.Decode(&msg); err != nil {
			if err == io.EOF {
				break
			}
			return nil, fmt.Errorf("error decoding stream message: %w", err)
		}

		var newMessage map[string]any
		if msg.SystemMessage != nil {
			if msg.SystemMessage.Text != nil {
				newMessage = handleTextResponse(ctx, msg.SystemMessage.Text)
			} else if msg.SystemMessage.Schema != nil {
				newMessage = handleSchemaResponse(ctx, msg.SystemMessage.Schema)
			} else if msg.SystemMessage.Data != nil {

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Log the raw response body (or replay the request with curl) to see what was actually returned instead of the JSON array.
  2. Bypass intermediaries (proxies, gateways, WAFs) that may rewrite or truncate the streaming response.
  3. Confirm the endpoint URL and API version are correct for Conversational Analytics.
  4. Retry, and if it persists, verify the response Content-Type is application/json and encoding is handled correctly.

Example fix

// before: assuming 200 means a JSON array
if resp.StatusCode != http.StatusOK { ... }
var messages []map[string]any
// after: defensively check content type before decoding
if ct := resp.Header.Get("Content-Type"); !strings.Contains(ct, "json") {
    return nil, fmt.Errorf("unexpected content-type: %s", ct)
}
var messages []map[string]any
Defensive patterns

Strategy: fallback

Validate before calling

// Go: validate content type before parsing the stream
func isJSONResponse(resp *http.Response) bool {
    return strings.Contains(resp.Header.Get("Content-Type"), "application/json")
}

Try / catch

result, err := invokeTool(ctx, req)
if err != nil && strings.Contains(err.Error(), "error reading start of json array") {
    log.Printf("non-JSON stream received, retrying once: %v", err)
    result, err = invokeTool(ctx, req) // intermediaries often intercept once
    if err != nil { return nil, fmt.Errorf("stream unreadable: %w", err) }
}

Prevention

When it happens

Trigger: The endpoint returned an HTML error page, a truncated response, a proxy-captured captive-portal page, or otherwise corrupted/non-JSON body while the status was still 200.

Common situations: A reverse proxy or API gateway intercepting the stream and returning HTML; response body truncated by a proxy/timeout mid-stream; wrong endpoint URL returning an unexpected payload; gzip/content-encoding mishandling by an intermediary.

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/e0afe86a92c3a7c7. Report an issue: GitHub.