{"record":{"id":"dabd0bcf319357e3","repo":"gastownhall/beads","slug":"record-dependency-removed-event-w","errorCode":null,"errorMessage":"record dependency_removed event: %w","messagePattern":"record dependency_removed event: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependencies.go","lineNumber":956,"sourceCode":"\t\treturn false, fmt.Errorf(\"lookup dependency type for %s -> %s: %w\", issueID, dependsOnID, err)\n\t}\n\n\tif _, err := tx.ExecContext(ctx, fmt.Sprintf(\n\t\t`DELETE FROM %s WHERE issue_id = ? AND %s = ?`, depTable, DepTargetExpr),\n\t\tissueID, dependsOnID); err != nil {\n\t\treturn false, fmt.Errorf(\"remove dependency: %w\", err)\n\t}\n\n\t// The lookup above returned early when no row matched, so reaching here means\n\t// an edge was actually deleted. Record the dependency_removed event on the\n\t// source issue's event table for bd CLI / library history observers — but only\n\t// when emitEvent is set, so structural removes stay silent (parity with the\n\t// proxied repo and with the symmetric AddDependencyInTx EmitEvent gate).\n\teventWritten := false\n\tif emitEvent {\n\t\tif err := RecordEventInTable(ctx, tx, eventTable, issueID, types.EventDependencyRemoved, actor,\n\t\t\tfmt.Sprintf(\"Removed dependency on %s\", dependsOnID)); err != nil {\n\t\t\treturn false, fmt.Errorf(\"record dependency_removed event: %w\", err)\n\t\t}\n\t\teventWritten = true\n\t}\n\n\tvar affectedIssues, affectedWisps []string\n\tvar aerr error\n\tif isWisp {\n\t\taffectedIssues, affectedWisps, aerr = AffectedByDepChangeForWispInTx(ctx, tx, issueID, dependsOnID, types.DependencyType(depType))\n\t} else {\n\t\taffectedIssues, affectedWisps, aerr = AffectedByDepChangeInTx(ctx, tx, issueID, dependsOnID, types.DependencyType(depType))\n\t}\n\tif aerr != nil {\n\t\treturn false, fmt.Errorf(\"affected by remove dependency %s -> %s: %w\", issueID, dependsOnID, aerr)\n\t}\n\trecomputed, err := RecomputeIsBlockedInTxWithResult(ctx, tx, affectedIssues, affectedWisps)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"recompute is_blocked after remove dependency %s -> %s: %w\", issueID, dependsOnID, err)\n\t}","sourceCodeStart":938,"sourceCodeEnd":974,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependencies.go#L938-L974","documentation":"This error wraps a failure from RecordEventInTable while writing the dependency_removed event row after a dependency edge was successfully deleted, when emitEvent was set (explicit 'bd dep remove'). The edge removal itself succeeded but the history event could not be recorded, so the transaction must roll back to keep the dependency table and event history consistent.","triggerScenarios":"RemoveDependencyInTx called with emitEvent=true and the INSERT into the source issue's event table (issues_events or wisps_events per routing) fails — event table missing, event table locked, connection loss, or schema drift on the events table columns.","commonSituations":"Old database missing the events table or newer event columns; event-table write contention from a concurrent bd process; disk full so the event INSERT fails after the DELETE; manual pruning that dropped event tables.","solutions":["Read the wrapped driver error from RecordEventInTable; retry the transaction if transient.","Run schema migrations to (re)create the events tables with expected columns.","Free disk space / resolve DB lock contention, then rerun 'bd dep remove'.","If you only need the structural removal without history, use the structural path (emitEvent=false), which skips event writing entirely."],"exampleFix":"// before: event insert fails on missing events table\nok, err := store.RemoveDependencyInTx(ctx, tx, \"bd-1\", \"bd-2\", actor, true)\n// after: migrate first, then remove\nif err := store.Migrate(ctx); err != nil { return err }\nok, err = store.RemoveDependencyInTx(ctx, tx, \"bd-1\", \"bd-2\", actor, true)","handlingStrategy":"try-catch","validationCode":"// ensure event tables exist and are writable before emitEvent removals\nfor _, t := range []string{\"issues_events\", \"wisps_events\"} {\n    if err := db.QueryRow(\"SELECT id FROM \" + t + \" LIMIT 1\").Err(); err != nil {\n        return fmt.Errorf(\"event table %s unavailable; migrate first: %w\", t, err)\n    }\n}","typeGuard":"func isEventRecordFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"record dependency_removed event\")\n}","tryCatchPattern":"written, err := store.RemoveDependencyInTx(ctx, tx, issueID, dependsOnID, actor, true)\nif isEventRecordFailure(err) {\n    if isTransient(errors.Unwrap(err)) { return retryTx(removalWithEvent) }\n    // else fall back to structural removal without history\n    written, err = store.RemoveDependencyInTx(ctx, tx, issueID, dependsOnID, actor, false)\n}","preventionTips":["Keep events tables in the migration set; never prune them manually.","If only the structural removal matters, use emitEvent=false to skip event writes.","Monitor disk space — event INSERTs fail after the edge DELETE on a full disk (transaction rolls back).","Retry the whole transaction on transient event-write failures; state stays consistent."],"tags":["storage","events","dependencies","transaction"],"backgroundTag":"event-record-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}