{"record":{"id":"0a1ae92ff77279b5","repo":"gastownhall/beads","slug":"batch-create-failed-d-of-d-issues-unconfirmed","errorCode":null,"errorMessage":"batch create failed; %d of %d issues unconfirmed (batch error: %v)","messagePattern":"batch create failed; (.+?) of (.+?) issues unconfirmed \\(batch error: (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/client.go","lineNumber":1084,"sourceCode":"\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}\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 unsuccessful; %d of %d issues unconfirmed\", len(chunk)-len(found), len(chunk))\n\t\t\t}","sourceCodeStart":1066,"sourceCodeEnd":1102,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/client.go#L1066-L1102","documentation":"In BatchCreateIssues, the issueBatchCreate request failed, recovery search ran successfully, but it confirmed fewer issues than the chunk contained — meaning some issues in the chunk were never created in Linear (or could not be found because their descriptions lack the idempotency marker). The method returns all issues confirmed so far plus this error stating how many of the chunk's issues are unconfirmed.","triggerScenarios":"Calling Client.BatchCreateIssues where c.Execute errors for a chunk, and recoverAfterAmbiguousBatch finds fewer issues than len(chunk) — typically because some inputs' descriptions have no <!-- bd-idempotency: ... --> marker (extractIdempotencyMarker returns \"\" and the issue is skipped), or the issues genuinely were never created.","commonSituations":"Passing IssueCreateInput values whose Description was built without the idempotency marker appended; network drops mid-batch so part of the chunk landed server-side and part did not; markers altered/escaped by downstream description transforms.","solutions":["Ensure every IssueCreateInput.Description includes the <!-- bd-idempotency: ... --> marker before calling BatchCreateIssues.","Retry the failed chunk with the same markers; Linear-side idempotent recovery will skip already-created issues.","Search Linear for the missing issues' markers manually to confirm which are truly absent.","Retry after resolving the transient network condition noted in the batch error portion."],"exampleFix":"// before\ninput := IssueCreateInput{Title: \"Bug\", Description: desc}\n// after\ninput := IssueCreateInput{Title: \"Bug\", Description: desc + \"\\n<!-- bd-idempotency: \" + marker + \" -->\"}","handlingStrategy":"retry","validationCode":"// Verify every input description carries a unique idempotency marker\nseen := map[string]bool{}\nfor _, in := range inputs {\n    m := extractMarker(in.Description) // must contain <!-- bd-idempotency: ... -->\n    if m == \"\" || seen[m] {\n        return errors.New(\"missing or duplicate idempotency marker in batch inputs\")\n    }\n    seen[m] = true\n}","typeGuard":"func hasUnconfirmedIssues(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"issues unconfirmed\")\n}","tryCatchPattern":"issues, err := client.BatchCreateIssues(ctx, inputs)\nif hasUnconfirmedIssues(err) {\n    // issues may hold confirmed ones; retry ONLY the missing items with the same markers\n    missing := diffByMarker(inputs, issues)\n    issues2, err2 := client.BatchCreateIssues(ctx, missing)\n}","preventionTips":["Guarantee the marker-generating code runs for every input before batching.","Use the same marker for the same logical issue across retries.","Reconcile via marker search instead of assuming all-or-nothing batch outcomes.","Log which chunk failed so partial results are tracked explicitly."],"tags":["batch","idempotency","network","graphql","partial-failure","linear-api"],"backgroundTag":"partial-batch-failure","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}