{"record":{"id":"13fd4a37f24bc153","repo":"gastownhall/beads","slug":"journal-dependency-removals-for-batched-wisp-delet","errorCode":null,"errorMessage":"journal dependency removals for batched wisp delete: %w","messagePattern":"journal dependency removals for batched wisp delete: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/wisps.go","lineNumber":451,"sourceCode":"\n\taffectedIssues, affectedWisps, aerr := issueops.AffectedByDeletionInTx(ctx, tx, nil, ids)\n\tif aerr != nil {\n\t\treturn 0, fmt.Errorf(\"affected by batched wisp delete: %w\", aerr)\n\t}\n\n\t// Resolve WHICH wisps this batch actually removes before the DELETE runs:\n\t// afterwards they are gone, and RowsAffected reports a count, not a set.\n\t// GC hands this path ids it scanned earlier, so an already-collected wisp is\n\t// a routine case, and a phantom delete record would tell a consumer to drop\n\t// a bead this transaction never touched.\n\tdeletedIDs, err := issueops.ExistingIssueIDsInTableInTx(ctx, tx, \"wisps\", ids)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"resolve existing wisps for batch delete: %w\", 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 := issueops.RecordDependencyRemovalsForIssuesInTx(ctx, tx, deletedIDs); err != nil {\n\t\treturn 0, fmt.Errorf(\"journal dependency removals for batched wisp delete: %w\", err)\n\t}\n\n\tinClause, args := doltBuildSQLInClause(ids)\n\n\t//nolint:gosec // G201: inClause contains only ? markers\n\tresult, err := tx.ExecContext(ctx,\n\t\tfmt.Sprintf(\"DELETE FROM wisps WHERE id IN (%s)\", inClause),\n\t\targs...)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to batch delete wisps: %w\", err)\n\t}\n\trowsAffected, _ := result.RowsAffected()\n\n\t// The batched wisp delete surface carries no actor, so the rows record none.\n\tfor _, id := range deletedIDs {\n\t\tif err := issueops.RecordDeleteInTx(ctx, tx, id, \"\"); err != nil {\n\t\t\treturn 0, err\n\t\t}","sourceCodeStart":433,"sourceCodeEnd":469,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/wisps.go#L433-L469","documentation":"This error wraps failures from issueops.RecordDependencyRemovalsForIssuesInTx, which journals dependency (edge) removals for the wisps about to be deleted while their source snapshots are still readable. This journaling must succeed before the rows are deleted so consumers can learn which edges disappeared; a failure here rolls back the whole batch transaction.","triggerScenarios":"DeleteWisps path on wisps that have dependency edges, when the INSERTs that journal edge removals fail: Dolt write timeout (batch too large or server slow), connection loss, cancelled context, or a missing/mismatched journal or dependencies schema.","commonSituations":"Large GC batches hitting Dolt's 10s write timeout on a slow disk or remote server; database migrated to a schema where journal tables are missing; concurrent writers causing lock contention on journal tables.","solutions":["Keep batches small — the library already batches at 200; if you call internals directly, stay within that limit to respect Dolt's 10s write timeout","Check Dolt server responsiveness (disk I/O, replication lag) and retry once healthy","Verify journal/dependencies tables exist with the expected schema; apply pending migrations","Inspect the wrapped cause for a specific driver error (lock timeout, ctx deadline) and tune timeouts accordingly"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Go: confirm journal tables exist before GC\nvar one int\nif err := db.QueryRowContext(ctx, \"SELECT 1 FROM issue_events LIMIT 1\").Scan(&one); err != nil {\n    return fmt.Errorf(\"journal table missing or unreachable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"_, err := store.DeleteWisps(ctx, ids)\nif err != nil {\n    if strings.Contains(err.Error(), \"journal dependency removals\") {\n        // journal write failed: check Dolt write timeout / server health before retry\n        log.Printf(\"edge journaling failed, batch rolled back: %v\", err)\n    }\n    return err\n}","preventionTips":["Keep delete batches at or below the library's 200-id limit to respect Dolt's 10s write timeout","Don't run concurrent GC and bulk mutations against the same database","Ensure schema migrations have created all journal tables","Give GC jobs generous context timeouts on slow storage"],"tags":["dolt","journal","transaction","gc"],"backgroundTag":"dependency-removal-journal-write-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}