charmbracelet/crush · error

failed to answer question batch: status code

Error message

failed to answer question batch: status code %d

What it means

AnswerQuestionBatch requires HTTP 200 from /workspaces/{id}/questions/answer. Any non-200 status produces this error with the code embedded. The server rejected the answer submission, so the question remains pending (or was rejected).

Solutions

  1. Read the status code in the error message and act: re-authenticate on 401/403, refresh the workspace id on 404, check server logs on 5xx.
  2. Validate the QuestionAnswer payload (IDs, format) against the current proto schema before sending.
  3. Re-authenticate if credentials expired.
  4. Retry after fixing the cause; check client/server version compatibility if 400 persists.
Defensive patterns

Strategy: try-catch

Validate before calling

// Sanity-check the answer payload before sending
if answer.ID == "" || len(answer.Answers) == 0 {
	return errors.New("question answer requires an id and at least one answer")
}

Try / catch

_, err := client.AnswerQuestionBatch(ctx, wsID, answer)
if err != nil && strings.Contains(err.Error(), "status code") {
	if isAuthStatus(err) { // 401/403
		return reauthenticateAndRetry(ctx, wsID, answer)
	}
	return fmt.Errorf("answering questions on %s: %w", wsID, err)
}

Prevention

When it happens

Trigger: POSTing answers with an invalid/expired workspace id (404), insufficient auth (401/403), a QuestionAnswer payload the server rejects (400), or a server fault (5xx).

Common situations: Workspace id stale after reconnect; token expiry mid-session; client/server protocol mismatch; server 500 while persisting the answer.

Related errors


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

Appendix: source

Thrown at internal/client/proto.go:706

	}
	var resp proto.PermissionGrantResponse
	if err := json.NewDecoder(rsp.Body).Decode(&resp); err != nil {
		return false, fmt.Errorf("failed to decode grant permission response: %w", err)
	}
	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 {

View on GitHub (pinned to 7944b8e522)