mattermost-community/focalboard · error

error when parsing response with code %d: %w

Error message

error when parsing response with code %d: %w

What it means

doAPIRequestReader returns this error when reading the body of a non-2xx HTTP response fails with an I/O error while constructing a RequestReaderError. The %d is the HTTP status code and %w preserves the underlying read error. Note the response (*http.Response) is still returned alongside the error, so status code details are available to the caller.

Source

Thrown at server/client/client.go:148

	if c.Token != "" {
		rq.Header.Set("Authorization", "Bearer "+c.Token)
	}

	rp, err := c.HTTPClient.Do(rq)
	if err != nil || rp == nil {
		return nil, err
	}

	if rp.StatusCode == http.StatusNotModified {
		return rp, nil
	}

	if rp.StatusCode >= http.StatusMultipleChoices {
		defer closeBody(rp)
		b, err := io.ReadAll(rp.Body)
		if err != nil {
			return rp, fmt.Errorf("error when parsing response with code %d: %w", rp.StatusCode, err)
		}
		return rp, RequestReaderError{b}
	}

	return rp, nil
}

func (c *Client) GetTeamRoute(teamID string) string {
	return fmt.Sprintf("%s/%s", c.GetTeamsRoute(), teamID)
}

func (c *Client) GetTeamsRoute() string {
	return "/teams"
}

func (c *Client) GetBlockRoute(boardID, blockID string) string {
	return fmt.Sprintf("%s/%s", c.GetBlocksRoute(boardID), blockID)
}

View on GitHub (pinned to a84bbb65e3)

Solutions

  1. Retry the request; the failure is typically transient connection-level
  2. Check network path/proxy timeouts and keep-alive settings between client and server
  3. Inspect the returned *http.Response (rp.StatusCode) even when err != nil to learn the status
  4. Verify server-side logs for why the error response was truncated

Example fix

// before
rp, err := client.DoAPIRequest(ctx, "GET", url, nil, nil)
if err != nil {
	return err
}
// after
rp, err := client.DoAPIRequest(ctx, "GET", url, nil, nil)
if err != nil {
	if rp != nil && rp.StatusCode >= 300 {
		return fmt.Errorf("API returned %d: %w", rp.StatusCode, err)
	}
	return err
}
Defensive patterns

Strategy: retry

Try / catch

rp, err := client.DoAPIRequest(ctx, method, url, header, body)
if err != nil {
	if rp != nil {
		// response still available; inspect rp.StatusCode
		if isTransientStatus(rp.StatusCode) {
			return retryRequest(ctx, method, url, header, body)
		}
	}
	return err
}

Prevention

When it happens

Trigger: Any API call routed through DoAPIRequest, TeamUploadFile, or ImportArchive that receives a status >= 300, and whose error-response body cannot be read — typically the server closed the connection mid-response, chunked encoding is truncated, or a proxy terminated the body stream.

Common situations: Unstable networks or timeouts between client and server; reverse proxies (nginx) cutting off error responses; server crashing while generating an error response; uploading large files where the connection drops before the error body completes.

Related errors


AI-assisted analysis of mattermost-community/focalboard@a84bbb65e3 (2026-08-30). Data as JSON: /api/errors/689dfff29b27ef7a. Report an issue: GitHub.