gastownhall/beads · error

failed to parse projects response: %w

Error message

failed to parse projects response: %w

What it means

This error means ListProjects successfully got an HTTP response, but the body could not be decoded into the library's generic list envelope (json.Unmarshal on the raw body failed). This usually indicates the response is not the expected {"value": [...], "count": n} JSON — e.g. an HTML error page, HTML login redirect, or truncated body.

Source

Thrown at internal/ado/client.go:577

	_, err := c.doRequest(ctx, http.MethodPatch, urlStr, "application/json-patch+json", ops)
	if err != nil {
		return fmt.Errorf("failed to remove work item link: %w", err)
	}
	return nil
}

// ListProjects returns all team projects in the organization.
// This is an org-level endpoint, not project-scoped.
func (c *Client) ListProjects(ctx context.Context) ([]Project, error) {
	urlStr := addAPIVersion(c.orgBase() + "/projects")
	respBody, err := c.doRequest(ctx, http.MethodGet, urlStr, "", nil)
	if err != nil {
		return nil, fmt.Errorf("failed to list projects: %w", err)
	}

	var envelope listResponse
	if err := json.Unmarshal(respBody, &envelope); err != nil {
		return nil, fmt.Errorf("failed to parse projects response: %w", err)
	}

	var projects []Project
	if err := json.Unmarshal(envelope.Value, &projects); err != nil {
		return nil, fmt.Errorf("failed to parse projects value: %w", err)
	}
	return projects, nil
}

// GetWorkItemTypes returns the work item types available in the project.
func (c *Client) GetWorkItemTypes(ctx context.Context) ([]WorkItemType, error) {
	urlStr := addAPIVersion(c.apiBase() + "/wit/workitemtypes")
	respBody, err := c.doRequest(ctx, http.MethodGet, urlStr, "", nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get work item types: %w", err)
	}

	var envelope listResponse

View on GitHub (pinned to 71377f2769)

Solutions

  1. Log the raw response body to see what was actually returned (HTML page? empty? partial JSON?)
  2. Verify you are not behind an auth proxy that intercepts the request and returns HTML
  3. Confirm the org URL is the ADO API endpoint, not the web UI host
  4. Retry — truncated bodies from transient network issues decode badly
  5. Update the library if ADO changed its API response shape for projects
Defensive patterns

Strategy: try-catch

Try / catch

projects, err := client.ListProjects(ctx)
if err != nil {
  if strings.Contains(err.Error(), "failed to parse projects response") {
    // non-JSON body: log it for diagnosis, likely proxy/auth HTML
    return nil, fmt.Errorf("non-JSON response from ADO (proxy or auth page?): %w", err)
  }
  return nil, err
}

Prevention

When it happens

Trigger: Calling ListProjects when the server returns 200/other with a non-JSON body: proxy/login HTML pages, HTML error pages behind a misconfigured URL, empty body, or BOM/encoding issues in the response.

Common situations: Corporate proxies returning an HTML auth page with 200; hitting the wrong URL that serves HTML; ADO outage returning an error page; TLS-terminating proxy mangling the response.

Understand the failure class

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/7c3beb62c981ae0c. Report an issue: GitHub.