{"record":{"id":"777afcfb46b3adde","repo":"gastownhall/beads","slug":"journal-dependency-removals-for-batch-delete-w","errorCode":null,"errorMessage":"journal dependency removals for batch delete: %w","messagePattern":"journal dependency removals for batch delete: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/delete.go","lineNumber":299,"sourceCode":"\t}\n\n\taffectedIssues, affectedWisps, aerr := AffectedByDeletionInTx(ctx, tx, set.RegularIDs, set.WispIDs)\n\tif aerr != nil {\n\t\treturn nil, fmt.Errorf(\"affected by batch delete: %w\", aerr)\n\t}\n\n\t// Resolve WHICH regular ids this delete actually removes before the batched\n\t// DELETE runs: afterwards the rows are gone, and RowsAffected reports a\n\t// count, not a set. A journal record for an id that was already absent would\n\t// tell a consumer to drop a bead this transaction never touched.\n\tjournaledDeletes, err := journalableDeletesInTx(ctx, tx, \"issues\", set.RegularIDs)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Edges are journaled before the rows go, while their source snapshots can\n\t// still be read.\n\tif err := RecordDependencyRemovalsForIssuesInTx(ctx, tx, set.All); err != nil {\n\t\treturn nil, fmt.Errorf(\"journal dependency removals for batch delete: %w\", err)\n\t}\n\n\tfor _, id := range set.WispIDs {\n\t\tif err := deleteIssueRowInTx(ctx, tx, id, true); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"delete wisp %s: %w\", id, err)\n\t\t}\n\t}\n\n\ttotalRegularsDeleted := 0\n\tfor i := 0; i < len(set.RegularIDs); i += deleteBatchSize {\n\t\tend := i + deleteBatchSize\n\t\tif end > len(set.RegularIDs) {\n\t\t\tend = len(set.RegularIDs)\n\t\t}\n\t\tbatch := set.RegularIDs[i:end]\n\t\tbatchInClause, batchArgs := buildSQLInClause(batch)\n\n\t\tdeleteResult, err := tx.ExecContext(ctx,","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/delete.go#L281-L317","documentation":"DeleteResolvedSetInTx aborts when RecordDependencyRemovalsForIssuesInTx fails to journal the dependency edges about to be removed. Journaling happens before the DELETE rows run so consumers can be told which edges vanished; if journaling fails, the delete is aborted rather than losing edge provenance. This is deliberately ordered before the row deletes.","triggerScenarios":"Calling DeleteIssuesInTx/DeleteInTx when the journal insert fails: journal table missing/full/corrupt, connection drop, unique-constraint conflicts on re-journaled edges, or context cancellation during the insert batch.","commonSituations":"Read-only database or insufficient write permissions on the journal table; disk full on embedded databases; a retry re-entering the delete after a partial failure causing journal key collisions; driver without journal table support.","solutions":["Unwrap the error to find the insert failure (constraint, permissions, disk).","Check disk space and write permissions on the database.","Clear/inspect stale journal rows if unique-constraint conflicts occur on retries.","Retry the delete in a fresh transaction — the abort is designed to leave data untouched."],"exampleFix":"// before\nres, err := store.DeleteIssuesInTx(ctx, tx, ids)\nif err != nil {\n    log.Fatal(err)\n}\n// after\nres, err := store.DeleteIssuesInTx(ctx, tx, ids)\nif err != nil {\n    log.Printf(\"delete aborted before mutation (journal failure): %v\", err)\n    return err\n}","handlingStrategy":"fallback","validationCode":"// ensure the journal table is writable and has space\nif err := db.PingContext(ctx); err != nil {\n    return fmt.Errorf(\"db unwritable before delete: %w\", err)\n}\nif err := checkDiskSpace(dataDir, 100<<20); err != nil {\n    return fmt.Errorf(\"insufficient disk for delete journal: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"res, err := store.DeleteIssuesInTx(ctx, tx, ids)\nif err != nil && strings.Contains(err.Error(), \"journal dependency removals\") {\n    return fmt.Errorf(\"delete aborted before mutation (safe to fix and retry): %w\", err)\n}","preventionTips":["Monitor disk space on embedded databases.","Avoid retry loops that re-enter a partially failed delete without inspecting journal state.","Run deletes with write access, never against read-only replicas."],"tags":["go","storage","transaction","journal","batch-delete"],"backgroundTag":"journal-write-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}