{"record":{"id":"5c12c38c5e466a94","repo":"gastownhall/beads","slug":"delete-resolve-ids-w","errorCode":null,"errorMessage":"delete: resolve ids: %w","messagePattern":"delete: resolve ids: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/delete_role.go","lineNumber":42,"sourceCode":"// BEFORE opening a transaction, so a malformed request costs no database work.\n//\n// THE REWRITE IS INSIDE THE TRANSACTION. A route that deleted the rows in one\n// transaction and rewrote the neighbors' text afterwards left, on a failure\n// between the two, a workspace whose rows were gone and whose descriptions\n// still cited them.\nfunc DeleteInTx(ctx context.Context, tx *sql.Tx, req publicops.DeleteRequest) (publicops.DeleteResult, error) {\n\tids := req.IDs\n\tresult := publicops.DeleteResult{DryRun: req.DryRun}\n\n\t// The existence probe comes FIRST, so `bd delete typo real` reports the\n\t// typo rather than whatever the graph says about the id that resolved.\n\twispSet, err := WispIDSetInTx(ctx, tx, ids)\n\tif err != nil {\n\t\treturn publicops.DeleteResult{}, fmt.Errorf(\"delete: classify planes: %w\", err)\n\t}\n\tfound, err := GetIssuesByIDsInTx(ctx, tx, ids, wispSet)\n\tif err != nil {\n\t\treturn publicops.DeleteResult{}, fmt.Errorf(\"delete: resolve ids: %w\", err)\n\t}\n\tpresent := make(map[string]bool, len(found))\n\tfor _, issue := range found {\n\t\tif issue != nil {\n\t\t\tpresent[issue.ID] = true\n\t\t}\n\t}\n\tvar missing []string\n\tfor _, id := range ids {\n\t\tif !present[id] {\n\t\t\tmissing = append(missing, id)\n\t\t}\n\t}\n\tif len(missing) > 0 {\n\t\treturn publicops.DeleteResult{}, &publicops.NotFoundError{IDs: missing}\n\t}\n\n\t// The version precondition sits between the existence probe and the","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/delete_role.go#L24-L60","documentation":"This error wraps a failure from GetIssuesByIDsInTx while DeleteInTx (the store-backed body of `bd delete`) resolves the requested ids to rows inside the delete transaction. It runs after the wisp-plane classification, so any SQL-level or driver-level failure during the batched id lookup surfaces here with the 'delete: resolve ids' prefix. It is an infrastructure wrapper, not a domain refusal — absent ids surface separately as a NotFoundError.","triggerScenarios":"DeleteInTx (or DeleteIssuesInTx via issueops.Deleter) calls GetIssuesByIDsInTx over the requested ids and the underlying query/scan/rows iteration fails — driver error, context cancellation mid-query, or a broken connection inside the open transaction.","commonSituations":"Database connection dropped or transaction invalidated before the probe; context deadline exceeded during a large --from-file batch; backend schema drift breaking the issues lookup; partially migrated database.","solutions":["Inspect the wrapped error (%w / errors.Unwrap) for the real driver message","Check database connectivity and that the transaction is still alive (no prior error/rollback)","Verify the storage schema is current (run migrations / `bd doctor`)","Retry the delete with a fresh transaction if the context was cancelled or the connection dropped","Reduce batch size or split large id lists if timeouts are the cause"],"exampleFix":"// before: unbounded ctx\nres, err := store.Delete(ctx, req)\n// after: bound the operation and handle context expiry\nctx, cancel := context.WithTimeout(ctx, 30*time.Second)\ndefer cancel()\nres, err := store.Delete(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"delete: resolve ids\") { /* check DB health, retry */ }","handlingStrategy":"try-catch","validationCode":"ids := normalizeDeleteIDs(req.IDs)\nif len(ids) == 0 { return errors.New(\"no ids to delete\") }\nif err := ctx.Err(); err != nil { return err } // ctx must be live before opening the tx","typeGuard":"var serr *publicops.NotFoundError\nif errors.As(err, &serr) { /* domain refusal, not an infra failure */ }\n// otherwise the 'delete: resolve ids' wrapper indicates a storage failure","tryCatchPattern":"res, err := store.Delete(ctx, req)\nif err != nil {\n    if strings.Contains(err.Error(), \"delete: resolve ids\") {\n        // storage-layer failure; check DB health, retry with fresh tx\n    }\n    return err\n}","preventionTips":["Always pass a live, adequately-budgeted context to Delete","Keep schema migrations current before running deletes","Monitor DB connectivity before batch deletes","Use DryRun first to exercise the read path before the real delete"],"tags":["database","storage","delete","wrapper-error"],"backgroundTag":"db-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}