gastownhall/beads · error

failed to parse teams response: %w

Error message

failed to parse teams response: %w

What it means

After fetching teams successfully at the transport level, FetchTeams unmarshals the JSON into TeamsResponse ({teams:{nodes:[...]}}). This error wraps json.Unmarshal failure — the body's shape didn't match the struct. The library throws it to separate decode problems from request problems.

Source

Thrown at internal/linear/client.go:1433

					name
					key
				}
			}
		}
	`

	req := &GraphQLRequest{
		Query: query,
	}

	data, err := c.Execute(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed to fetch teams: %w", err)
	}

	var teamsResp TeamsResponse
	if err := json.Unmarshal(data, &teamsResp); err != nil {
		return nil, fmt.Errorf("failed to parse teams response: %w", err)
	}

	return teamsResp.Teams.Nodes, nil
}

// FetchProjects retrieves projects from Linear with optional filtering by state.
// state can be: "planned", "started", "paused", "completed", "canceled", or "all"/"".
func (c *Client) FetchProjects(ctx context.Context, state string) ([]Project, error) {
	var allProjects []Project
	var cursor string

	filter := map[string]interface{}{
		"team": map[string]interface{}{
			"id": map[string]interface{}{
				"eq": c.TeamID,
			},
		},
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Dump the raw `data` payload to inspect the actual JSON structure.
  2. Check whether the body contains a GraphQL "errors" array and surface that instead of unmarshalling into TeamsResponse.
  3. Diff the Team/TeamsResponse structs against the current Linear GraphQL schema and update field names/types.
  4. Bypass any proxy/gateway to confirm the body is genuine GraphQL JSON.
  5. Update the library/client to a version matching the current Linear API schema.

Example fix

// before
var teamsResp TeamsResponse
if err := json.Unmarshal(data, &teamsResp); err != nil { return nil, err }
// after
var probe map[string]json.RawMessage
if err := json.Unmarshal(data, &probe); err != nil { return nil, err }
if _, ok := probe["teams"]; !ok {
    return nil, fmt.Errorf("unexpected teams payload: %s", truncate(string(data), 200))
}
var teamsResp TeamsResponse
if err := json.Unmarshal(data, &teamsResp); err != nil { return nil, err }
Defensive patterns

Strategy: type-guard

Type guard

func isTeamsResponse(data []byte) bool {
    var probe struct {
        Teams *struct{ Nodes []json.RawMessage `json:"nodes"` } `json:"teams"`
    }
    return json.Unmarshal(data, &probe) == nil && probe.Teams != nil
}

Try / catch

teams, err := client.FetchTeams(ctx)
if err != nil {
    var ute *json.UnmarshalTypeError
    if errors.As(err, &ute) {
        return fmt.Errorf("linear schema drift in teams response (field %s): %w", ute.Field, err)
    }
    return err
}

Prevention

When it happens

Trigger: c.Execute succeeded but the payload isn't the expected shape: response is a GraphQL errors envelope with HTTP 200, a proxy returned an HTML page, or TeamsResponse/Team structs no longer match the Linear schema (field renamed, type changed, nullability mismatch causing decode error).

Common situations: Linear API schema evolution breaking generated structs; corporate TLS-inspecting proxy substituting an error page; an API gateway returning JSON error objects; version mismatch between client library and Linear API version.

Understand the failure class

Related errors


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