gastownhall/beads · error

failed to parse description search response: %w

Error message

failed to parse description search response: %w

What it means

Once the description-search query succeeds, the response bytes are unmarshalled into IssuesResponse. This error wraps a JSON unmarshal failure, meaning the HTTP call worked but the body does not match the expected `{ issues: { nodes: [...] } }` shape.

Source

Thrown at internal/linear/client.go:786

			"contains": text,
		},
	}

	req := &GraphQLRequest{
		Query: query,
		Variables: map[string]interface{}{
			"filter": filter,
		},
	}

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

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

	if len(issuesResp.Issues.Nodes) > 0 {
		return &issuesResp.Issues.Nodes[0], nil
	}
	return nil, nil
}

// issueCreateMutation is the GraphQL mutation for creating a Linear issue.
const issueCreateMutation = `
	mutation CreateIssue($input: IssueCreateInput!) {
		issueCreate(input: $input) {
			success
			issue {
				id
				identifier
				title
				description

View on GitHub (pinned to 71377f2769)

Solutions

  1. Log the raw response body alongside the unmarshal error to see the actual payload.
  2. Confirm the endpoint URL is https://api.linear.app/graphql.
  3. Check for and surface any GraphQL `errors` array in the response before/instead of unmarshalling.
  4. Update IssuesResponse struct fields if the Linear schema changed.

Example fix

// before
if err := json.Unmarshal(data, &issuesResp); err != nil {
    return nil, fmt.Errorf("failed to parse description search response: %w", err)
}
// after
if err := json.Unmarshal(data, &issuesResp); err != nil {
    return nil, fmt.Errorf("failed to parse description search response: %w (body: %.200s)", err, data)
}
Defensive patterns

Strategy: type-guard

Validate before calling

// verify endpoint before search
if client.Endpoint != "https://api.linear.app/graphql" {
    return errors.New("unexpected Linear endpoint")
}

Type guard

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

Try / catch

issue, err := client.FindIssueByDescriptionContains(ctx, desc)
if err != nil {
    var typeErr *json.UnmarshalTypeError
    if errors.As(err, &typeErr) {
        log.Printf("unexpected search response shape at %s; skipping dedupe", typeErr.Field)
        return nil // proceed without dedupe or use cache
    }
    return err
}

Prevention

When it happens

Trigger: Linear returns 200 with a body that doesn't fit IssuesResponse — GraphQL error partial payloads, schema changes to the issues connection, proxy/HTML error bodies with a 200 status, or truncated responses.

Common situations: Hitting the wrong endpoint (REST instead of /graphql), corporate proxies returning HTML interstitials, Linear schema updates renaming `issues` or `nodes`, mid-request connection drops producing truncated JSON.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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