charmbracelet/crush · error

failed to decode answer question batch response: %w

Error message

failed to decode answer question batch response: %w

What it means

On a 200 response, AnswerQuestionBatch decodes the body into proto.QuestionAnswerResponse to learn whether this call resolved the question. Decode failure (empty body, HTML from a proxy, schema mismatch) yields this error, leaving the Resolved outcome unknown.

Source

Thrown at internal/client/proto.go:710

	}
	return resp.Resolved, nil
}

// AnswerQuestionBatch submits answers for a batch question on a
// workspace. Returns true if this call resolved the pending
// request, false if already resolved by another caller.
func (c *Client) AnswerQuestionBatch(ctx context.Context, id string, req proto.QuestionAnswer) (bool, error) {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/questions/answer", id), nil, jsonBody(req), http.Header{"Content-Type": []string{"application/json"}})
	if err != nil {
		return false, fmt.Errorf("failed to answer question batch: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return false, fmt.Errorf("failed to answer question batch: status code %d", rsp.StatusCode)
	}
	var resp proto.QuestionAnswerResponse
	if err := json.NewDecoder(rsp.Body).Decode(&resp); err != nil {
		return false, fmt.Errorf("failed to decode answer question batch response: %w", err)
	}
	return resp.Resolved, nil
}

// CancelQuestionBatch cancels the pending question batch on a
// workspace. Returns true if a question was cancelled, false if
// none was pending.
func (c *Client) CancelQuestionBatch(ctx context.Context, id string) (bool, error) {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/questions/cancel", id), nil, nil, http.Header{})
	if err != nil {
		return false, fmt.Errorf("failed to cancel question batch: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return false, fmt.Errorf("failed to cancel question batch: status code %d", rsp.StatusCode)
	}
	var resp proto.QuestionAnswerResponse
	if err := json.NewDecoder(rsp.Body).Decode(&resp); err != nil {

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Capture the raw response body (curl or logging proxy) to identify the malformed payload.
  2. Align client and server versions so the response struct matches.
  3. Inspect intermediary proxies/gateways that may alter response bodies.
  4. Retry; if reproducible, file a server bug with a wire dump.
Defensive patterns

Strategy: retry

Type guard

func isAnswerDecodeError(err error) bool {
	return err != nil && strings.Contains(err.Error(), "failed to decode answer question batch response")
}

Try / catch

resolved, err := client.AnswerQuestionBatch(ctx, wsID, answer)
if isAnswerDecodeError(err) {
	// outcome unknown; retry once — Resolved flag prevents double resolution
	return retryAnswerBatch(ctx, wsID, answer, 1)
}

Prevention

When it happens

Trigger: Server returns 200 with empty/malformed JSON; a proxy rewrites the body; the server's response schema differs from proto.QuestionAnswerResponse due to version skew.

Common situations: Gateway HTML interstitial with 200; partial/truncated response on flaky connections; server upgraded to a new response format while client is old.

Understand the failure class

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/15d1f224d2e81772. Report an issue: GitHub.