{"record":{"id":"81f4a4dd0f2945a8","repo":"gastownhall/beads","slug":"failed-to-remove-relates-to-s-s-w","errorCode":null,"errorMessage":"failed to remove relates-to %s -> %s: %w","messagePattern":"failed to remove relates-to (.+?) -> (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/relate.go","lineNumber":194,"sourceCode":"\tissue2, err = store.GetIssue(ctx, id2)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get issue %s: %w\", id2, err)\n\t}\n\n\tif issue1 == nil {\n\t\treturn fmt.Errorf(\"issue not found: %s\", id1)\n\t}\n\tif issue2 == nil {\n\t\treturn fmt.Errorf(\"issue not found: %s\", id2)\n\t}\n\n\t// Remove relates-to dependency in both directions\n\t// Per Decision 004, relates-to links are now stored in dependencies table.\n\t// bd unrelate is an explicit dependency verb, so it records history\n\t// (EmitEvent) like bd dep remove; only structural teardown stays silent.\n\t// Remove id1 -> id2\n\tif err := store.RemoveDependencyWithOptions(ctx, id1, id2, actor, storage.DependencyRemoveOptions{EmitEvent: true}); err != nil {\n\t\treturn fmt.Errorf(\"failed to remove relates-to %s -> %s: %w\", id1, id2, err)\n\t}\n\t// Remove id2 -> id1 (bidirectional)\n\tif err := store.RemoveDependencyWithOptions(ctx, id2, id1, actor, storage.DependencyRemoveOptions{EmitEvent: true}); err != nil {\n\t\treturn fmt.Errorf(\"failed to remove relates-to %s -> %s: %w\", id2, id1, err)\n\t}\n\n\tif jsonOutput {\n\t\tresult := map[string]interface{}{\n\t\t\t\"id1\":       id1,\n\t\t\t\"id2\":       id2,\n\t\t\t\"unrelated\": true,\n\t\t}\n\t\tencoder := json.NewEncoder(os.Stdout)\n\t\tencoder.SetIndent(\"\", \"  \")\n\t\treturn encoder.Encode(result)\n\t}\n\n\tfmt.Printf(\"%s Unlinked %s ↔ %s\\n\", ui.RenderPass(\"✓\"), id1, id2)","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/relate.go#L176-L212","documentation":"runUnrelate removes the relates-to dependency in both directions using store.RemoveDependencyWithOptions with EmitEvent: true (per Decision 004, relates-to links live in the dependencies table and unrelate records history). This wraps a storage error from the first removal, id1 -> id2. Both issues were already verified to exist, so this is a dependency-table write failure, not a missing record.","triggerScenarios":"store.RemoveDependencyWithOptions(ctx, id1, id2, actor, {EmitEvent:true}) errors: database locked by a concurrent writer, constraint or driver error deleting the dependency row, EmitEvent history write failing, or storage corruption in the dependencies table.","commonSituations":"Concurrent `bd dep`/sync operations holding a write lock; a half-migrated dependencies table (older bd version created the data); disk I/O failure during the delete-plus-event transaction; corrupted Dolt store after a crash.","solutions":["Read the wrapped inner error to identify the storage failure (lock vs constraint vs I/O) and address that cause directly.","If it is a lock, wait for the other bd process / `bd dolt` command to finish and rerun the unrelate; serialize writers.","Run `bd doctor` to check database health; if the dependencies table is inconsistent, follow its repair guidance or restore from backup and re-sync.","Ensure both endpoints use consistent bd versions — upgrade/downgrade so the writer matches the schema that created the dependency rows.","Retry after transient I/O failures; the removal is a single transaction so a partial delete-without-event should not persist."],"exampleFix":"// before\n$ bd unrelate bd-1 bd-2 &  bd dolt push &   # concurrent writers\nfailed to remove relates-to bd-1 -> bd-2: database is locked\n// after\n$ bd dolt push && bd unrelate bd-1 bd-2     # run storage ops sequentially","handlingStrategy":"try-catch","validationCode":"// both issues verified to exist; check storage writability before mutating\nif err := store.RunInTransaction(ctx, func(tx storage.Tx) error { return nil }); err != nil {\n\treturn fmt.Errorf(\"storage not writable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"err := store.RemoveDependencyWithOptions(ctx, id1, id2, actor, storage.DependencyRemoveOptions{EmitEvent: true})\nif err != nil {\n\tif errors.Is(err, ErrDatabaseLocked) {\n\t\t// serialize with other writers, then retry once\n\t\ttime.Sleep(time.Second)\n\t\terr = store.RemoveDependencyWithOptions(ctx, id1, id2, actor, storage.DependencyRemoveOptions{EmitEvent: true})\n\t}\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to remove relates-to %s -> %s: %w\", id1, id2, err)\n\t}\n}","preventionTips":["Avoid running bd mutations concurrently with `bd dolt push/pull` in the same repo.","Keep bd versions consistent across machines touching the same database to avoid schema skew.","Run `bd doctor` after unexpected crashes to catch dependencies-table corruption early.","Back up or rely on git-synced export before bulk unrelate operations."],"tags":["go","storage","dependencies","unrelate","database"],"backgroundTag":"dependency-removal-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}