gastownhall/beads · error

batch update unsuccessful, single-issue fallback also failed

Error message

batch update unsuccessful, single-issue fallback also failed for %s: %w

What it means

BatchUpdateIssues sends chunked issueBatchUpdate mutations; when Linear reports success=false for a chunk, the client retries each issue individually via UpdateIssue. This error wraps the failure of that per-issue fallback, meaning both the batch mutation was rejected AND the single-issue update for this specific ID also failed.

Source

Thrown at internal/linear/client.go:1215

				issue, updateErr := c.UpdateIssue(ctx, id, updates)
				if updateErr != nil {
					return allIssues, fmt.Errorf("batch update failed, single-issue fallback also failed for %s: %w (batch error: %v)", id, updateErr, err)
				}
				allIssues = append(allIssues, *issue)
			}
			continue
		}

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

		if !batchResp.IssueBatchUpdate.Success {
			for _, id := range chunk {
				issue, updateErr := c.UpdateIssue(ctx, id, updates)
				if updateErr != nil {
					return allIssues, fmt.Errorf("batch update unsuccessful, single-issue fallback also failed for %s: %w", id, updateErr)
				}
				allIssues = append(allIssues, *issue)
			}
			continue
		}

		allIssues = append(allIssues, batchResp.IssueBatchUpdate.Issues...)
	}

	return allIssues, nil
}

// FetchIssueByIdentifier retrieves a single issue from Linear by its identifier (e.g., "TEAM-123").
// Returns nil if the issue is not found.
func (c *Client) FetchIssueByIdentifier(ctx context.Context, identifier string) (*Issue, error) {
	query := `
		query IssueByIdentifier($filter: IssueFilter!) {
			issues(filter: $filter, first: 1) {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped updateErr (use %v/%+v or errors.Unwrap) to see why the single-issue update failed — it is the root cause.
  2. Verify every key/value in the updates map is valid for the target team (state IDs, label IDs, assignee IDs).
  3. Re-run BatchUpdateIssues: issues earlier in the chunk may have been updated already; the library does not roll them back.
  4. Check the Linear API token has write scopes for the affected team/workspace.
  5. If the batch consistently fails but singles succeed, reduce BatchSize or check for Linear-side validation rules blocking batched mutations.

Example fix

// before
updates := map[string]interface{}{"stateId": "stale-state-id"}
_, err := client.BatchUpdateIssues(ctx, ids, updates)
// after
state := cache.FindStateForBeadsStatus(types.Open) // resolve a fresh, team-scoped state ID
if state == "" { return fmt.Errorf("no valid state for team") }
updates := map[string]interface{}{"stateId": state}
_, err := client.BatchUpdateIssues(ctx, ids, updates)
Defensive patterns

Strategy: fallback

Validate before calling

// pre-validate IDs and update payload before batching
for _, id := range ids {
    if !strings.HasPrefix(id, "issue-") { return fmt.Errorf("invalid issue id: %s", id) }
}
if sid, ok := updates["stateId"].(string); ok && cache.FindStateByID(sid) == nil {
    return fmt.Errorf("stateId %q not in team states", sid)
}

Try / catch

all, err := client.BatchUpdateIssues(ctx, ids, updates)
if err != nil {
    var parseErr *json.SyntaxError
    if errors.As(err, &parseErr) { /* decode issue */ }
    // err wraps the failing issue id; retry remaining ids individually with backoff
    log.Printf("batch update failed: %v", err)
    return retryIndividually(ids, updates)
}

Prevention

When it happens

Trigger: Linear accepted the GraphQL request (Execute succeeded) but returned IssueBatchUpdate.Success=false — e.g. an invalid state ID, label ID, or field value in the updates map — and then UpdateIssue(ctx, id, updates) also returned an error (invalid ID, permission denied, another GraphQL error) for the issue being retried.

Common situations: Syncing issues to a state ID that was archived or belongs to a different team; passing label IDs from another workspace; API token lacking write permission to the team; partial failure mid-chunk so earlier issues in the chunk were already updated individually (partial application).

Related errors


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