gastownhall/beads · critical

batch create failed and recovery search also failed: %w (bat

Error message

batch create failed and recovery search also failed: %w (batch error: %v)

What it means

During BatchCreateIssues, the issueBatchCreate HTTP request failed (err from c.Execute) AND the idempotency recovery search (recoverAfterAmbiguousBatch, which looks for the bd-idempotency marker in descriptions) also failed. Because Linear may have partially applied the batch, the client cannot tell which issues exist, so it returns all issues confirmed so far (allIssues) plus this combined error wrapping both the recovery error and the original batch error.

Source

Thrown at internal/linear/client.go:1080

		if end > len(inputs) {
			end = len(inputs)
		}
		chunk := inputs[start:end]

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

		data, err := c.Execute(ctx, req)
		if err != nil {
			found, recoverErr := c.recoverAfterAmbiguousBatch(ctx, chunk)
			if recoverErr != nil {
				return allIssues, fmt.Errorf("batch create failed and recovery search also failed: %w (batch error: %v)", recoverErr, err)
			}
			allIssues = append(allIssues, found...)
			if len(found) < len(chunk) {
				return allIssues, fmt.Errorf("batch create failed; %d of %d issues unconfirmed (batch error: %v)", len(chunk)-len(found), len(chunk), err)
			}
			continue
		}

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

		if !batchResp.IssueBatchCreate.Success {
			found, recoverErr := c.recoverAfterAmbiguousBatch(ctx, chunk)
			if recoverErr != nil {
				return allIssues, fmt.Errorf("batch create unsuccessful and recovery search also failed: %w", recoverErr)
			}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped recovery error (%w) first — it is the primary cause; the batch error is appended as context.
  2. Wait for rate limits to reset, then re-run recovery/idempotent batch creation; issues from earlier chunks already returned are safe.
  3. Search Linear manually for the idempotency markers of the affected chunk to reconcile duplicates.
  4. Verify API token validity and network reachability to api.linear.app.

Example fix

// before
issues, err := client.BatchCreateIssues(ctx, inputs)
if err != nil {
    return err // duplicates possible if blindly retried
}
// after
issues, err := client.BatchCreateIssues(ctx, inputs)
if err != nil {
    if strings.Contains(err.Error(), "recovery search also failed") {
        // do NOT re-create blindly; reconcile via marker search first
        return fmt.Errorf("ambiguous batch outcome, reconcile before retry: %w", err)
    }
    return err
}
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check connectivity and auth before batch creation
if err := client.CheckAuth(ctx); err != nil {
    return fmt.Errorf("linear unreachable/unauthorized, aborting batch: %w", err)
}
// Ensure every input carries the idempotency marker
for _, in := range inputs {
    if !strings.Contains(in.Description, "<!-- bd-idempotency:") {
        return errors.New("all batch inputs must embed the bd-idempotency marker")
    }
}

Type guard

func isAmbiguousBatchFailure(err error) bool {
    return err != nil && strings.Contains(err.Error(), "recovery search also failed")
}

Try / catch

issues, err := client.BatchCreateIssues(ctx, inputs)
if isAmbiguousBatchFailure(err) {
    // Do NOT blindly re-create; wait/backoff, then re-run with the SAME markers
    <-time.After(backoff)
    issues, err = client.BatchCreateIssues(ctx, inputs)
}

Prevention

When it happens

Trigger: Calling Client.BatchCreateIssues when the Execute call fails (network error, timeout, GraphQL error) and the subsequent per-issue FindIssueByDescriptionContains search inside recoverAfterAmbiguousBatch also errors (e.g. the search API is also unreachable or rate-limited).

Common situations: Linear outage or rate limiting affecting both mutation and search endpoints; expired API token causing both calls to fail; inputs whose descriptions lack the idempotency marker combined with a search failure.

Related errors


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