{"record":{"id":"baf89f4f30f42407","repo":"gastownhall/beads","slug":"batch-create-failed-and-recovery-search-also-faile","errorCode":null,"errorMessage":"batch create failed and recovery search also failed: %w (batch error: %v)","messagePattern":"batch create failed and recovery search also failed: %w \\(batch error: (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/linear/client.go","lineNumber":1080,"sourceCode":"\t\tif end > len(inputs) {\n\t\t\tend = len(inputs)\n\t\t}\n\t\tchunk := inputs[start:end]\n\n\t\treq := &GraphQLRequest{\n\t\t\tQuery: query,\n\t\t\tVariables: map[string]interface{}{\n\t\t\t\t\"input\": map[string]interface{}{\n\t\t\t\t\t\"issues\": chunk,\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\n\t\tdata, err := c.Execute(ctx, req)\n\t\tif err != nil {\n\t\t\tfound, recoverErr := c.recoverAfterAmbiguousBatch(ctx, chunk)\n\t\t\tif recoverErr != nil {\n\t\t\t\treturn allIssues, fmt.Errorf(\"batch create failed and recovery search also failed: %w (batch error: %v)\", recoverErr, err)\n\t\t\t}\n\t\t\tallIssues = append(allIssues, found...)\n\t\t\tif len(found) < len(chunk) {\n\t\t\t\treturn allIssues, fmt.Errorf(\"batch create failed; %d of %d issues unconfirmed (batch error: %v)\", len(chunk)-len(found), len(chunk), err)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tvar batchResp IssueBatchCreateResponse\n\t\tif err := json.Unmarshal(data, &batchResp); err != nil {\n\t\t\treturn allIssues, fmt.Errorf(\"failed to parse batch create response: %w\", err)\n\t\t}\n\n\t\tif !batchResp.IssueBatchCreate.Success {\n\t\t\tfound, recoverErr := c.recoverAfterAmbiguousBatch(ctx, chunk)\n\t\t\tif recoverErr != nil {\n\t\t\t\treturn allIssues, fmt.Errorf(\"batch create unsuccessful and recovery search also failed: %w\", recoverErr)\n\t\t\t}","sourceCodeStart":1062,"sourceCodeEnd":1098,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/client.go#L1062-L1098","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Inspect the wrapped recovery error (%w) first — it is the primary cause; the batch error is appended as context.","Wait for rate limits to reset, then re-run recovery/idempotent batch creation; issues from earlier chunks already returned are safe.","Search Linear manually for the idempotency markers of the affected chunk to reconcile duplicates.","Verify API token validity and network reachability to api.linear.app."],"exampleFix":"// before\nissues, err := client.BatchCreateIssues(ctx, inputs)\nif err != nil {\n    return err // duplicates possible if blindly retried\n}\n// after\nissues, err := client.BatchCreateIssues(ctx, inputs)\nif err != nil {\n    if strings.Contains(err.Error(), \"recovery search also failed\") {\n        // do NOT re-create blindly; reconcile via marker search first\n        return fmt.Errorf(\"ambiguous batch outcome, reconcile before retry: %w\", err)\n    }\n    return err\n}","handlingStrategy":"retry","validationCode":"// Pre-check connectivity and auth before batch creation\nif err := client.CheckAuth(ctx); err != nil {\n    return fmt.Errorf(\"linear unreachable/unauthorized, aborting batch: %w\", err)\n}\n// Ensure every input carries the idempotency marker\nfor _, in := range inputs {\n    if !strings.Contains(in.Description, \"<!-- bd-idempotency:\") {\n        return errors.New(\"all batch inputs must embed the bd-idempotency marker\")\n    }\n}","typeGuard":"func isAmbiguousBatchFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"recovery search also failed\")\n}","tryCatchPattern":"issues, err := client.BatchCreateIssues(ctx, inputs)\nif isAmbiguousBatchFailure(err) {\n    // Do NOT blindly re-create; wait/backoff, then re-run with the SAME markers\n    <-time.After(backoff)\n    issues, err = client.BatchCreateIssues(ctx, inputs)\n}","preventionTips":["Embed the bd-idempotency marker in every issue description before batching.","Apply exponential backoff on Linear rate-limit (429) responses.","Treat already-returned issues from earlier chunks as created; never re-include them.","Monitor Linear status/auth so combined mutation+search failures are detected early."],"tags":["network","graphql","batch","idempotency","rate-limit","linear-api"],"backgroundTag":"ambiguous-batch-outcome","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}