charmbracelet/crush · error

failed to decode import copilot response: %w

Error message

failed to decode import copilot response: %w

What it means

After a 200 response, ImportCopilot decodes the JSON body containing the imported token and success flag. If the body is not valid JSON or does not match the expected shape, this error wraps the decode failure.

Source

Thrown at internal/client/config.go:143

}

// ImportCopilot attempts to import a GitHub Copilot token on the
// server.
func (c *Client) ImportCopilot(ctx context.Context, id string) (*oauth.Token, bool, error) {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/config/import-copilot", id), nil, nil, nil)
	if err != nil {
		return nil, false, fmt.Errorf("failed to import copilot: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return nil, false, fmt.Errorf("failed to import copilot: status code %d", rsp.StatusCode)
	}
	var result struct {
		Token   *oauth.Token `json:"token"`
		Success bool         `json:"success"`
	}
	if err := json.NewDecoder(rsp.Body).Decode(&result); err != nil {
		return nil, false, fmt.Errorf("failed to decode import copilot response: %w", err)
	}
	return result.Token, result.Success, nil
}

// RefreshOAuthToken refreshes an OAuth token for a provider on the
// server.
func (c *Client) RefreshOAuthToken(ctx context.Context, id string, scope config.Scope, providerID string) error {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/config/refresh-oauth", id), nil, jsonBody(struct {
		Scope      config.Scope `json:"scope"`
		ProviderID string       `json:"provider_id"`
	}{Scope: scope, ProviderID: providerID}), http.Header{"Content-Type": []string{"application/json"}})
	if err != nil {
		return fmt.Errorf("failed to refresh OAuth token: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to refresh OAuth token: status code %d", rsp.StatusCode)
	}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Inspect the wrapped %w error and capture the raw response body for diagnosis
  2. Check whether a proxy or middleware is rewriting the response
  3. Ensure client and server versions are compatible
  4. Retry; if persistent, report a server bug returning invalid JSON with 200
Defensive patterns

Strategy: type-guard

Validate before calling

// sanity-check the endpoint returns JSON before decoding
dump, _ := c.ImportCopilot(ctx, wsID) // if this errors with decode, log raw body via server logs

Type guard

func isJSONContentType(h http.Header) bool {
    return strings.Contains(h.Get("Content-Type"), "application/json")
}

Try / catch

tok, ok, err := c.ImportCopilot(ctx, wsID)
if err != nil {
    if strings.Contains(err.Error(), "failed to decode") {
        // likely a proxy or server bug returning non-JSON with 200
        return diagnoseRawResponse(ctx, wsID)
    }
    return err
}

Prevention

When it happens

Trigger: Server returns 200 with malformed, empty, or non-JSON body for import-copilot (e.g. a proxy intercepting the response, or server bug returning an error page with 200).

Common situations: Reverse proxy or captive portal returning HTML with status 200; server/client version mismatch changing the response schema; truncated response.

Understand the failure class

Related errors


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