gastownhall/beads · error

failed to update project: %w

Error message

failed to update project: %w

What it means

UpdateProject wraps any error returned by the GraphQL Execute call (network failure, HTTP error, or GraphQL errors array) with the context 'failed to update project'. The underlying error is preserved via %w so callers can unwrap with errors.Is/As. It signals the Linear API rejected or never received the project mutation.

Source

Thrown at internal/linear/client.go:1579

					state
					progress
					updatedAt
				}
			}
		}
	`

	req := &GraphQLRequest{
		Query: query,
		Variables: map[string]interface{}{
			"id":    projectID,
			"input": updates,
		},
	}

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

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

	if !updateResp.ProjectUpdate.Success {
		return nil, fmt.Errorf("project update reported as unsuccessful")
	}

	return &updateResp.ProjectUpdate.Project, nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error with errors.Unwrap (or %v logging) to see the actual transport/GraphQL failure.
  2. Verify the Linear API key is valid and has access to the target project/team.
  3. Check that the project ID passed to UpdateProject still exists (fetch it first via the API).
  4. Retry with backoff if the wrapped error is a 429 rate-limit or transient network error.
  5. Check Linear's status page for API outages.

Example fix

// before
project, err := client.UpdateProject(ctx, id, updates)
if err != nil {
	log.Fatal(err)
}
// after
project, err := client.UpdateProject(ctx, id, updates)
if err != nil {
	var gqlErr *linear.GraphQLError
	if errors.As(err, &gqlErr) {
		log.Fatalf("linear rejected project update: %v", gqlErr)
	}
	if errors.Is(err, context.DeadlineExceeded) {
		log.Fatal("linear API timed out; retry later")
	}
	log.Fatal(err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if projectID == "" {
	return fmt.Errorf("project ID required before UpdateProject")
}
if client == nil || apiKey == "" {
	return fmt.Errorf("linear client/key must be configured before UpdateProject")
}

Try / catch

project, err := client.UpdateProject(ctx, id, updates)
if err != nil {
	var netErr net.Error
	switch {
	case errors.As(err, &netErr) || errors.Is(err, context.DeadlineExceeded):
		// retry with backoff
	case strings.Contains(err.Error(), "429"):
		// honor rate limit, retry later
	default:
		return fmt.Errorf("update project %s: %w", id, err)
	}
}

Prevention

When it happens

Trigger: Calling linear Client.UpdateProject when the Linear API is unreachable, the API key is invalid/expired, the project ID does not exist, a rate limit is hit, or the GraphQL response carries an errors array.

Common situations: Revoked Linear API keys, offline/timeout network conditions, updating a project that was deleted or that the integration token cannot access, Linear API downtime, exceeded GraphQL complexity or rate limits.

Related errors


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