{"record":{"id":"f92dbd6d18798272","repo":"gastownhall/beads","slug":"w-s-cannot-depend-on-itself-f92dbd","errorCode":null,"errorMessage":"%w: %s cannot depend on itself","messagePattern":"%w: (.+?) cannot depend on itself","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependencies.go","lineNumber":443,"sourceCode":"\t\t  AND s.is_blocked = 0\n\t\t  AND s.status <> 'closed' AND s.status <> 'pinned'\n\t\t  AND EXISTS (\n\t\t    SELECT 1 FROM (\n\t\t      SELECT id, status FROM %s WHERE id = ?\n\t\t    ) AS t\n\t\t    WHERE t.status <> 'closed' AND t.status <> 'pinned'\n\t\t  )\n\t`, sourceTable, targetTable), source, target)\n\treturn err\n}\n\n// CheckDependencyCycleInTx rejects self-dependencies and cycles across the\n// combined blocks, conditional-blocks, and parent-child graph before insert.\n// The caller may pass a restricted depTables list for a known storage bucket;\n// nil uses all dependency tables.\nfunc CheckDependencyCycleInTx(ctx context.Context, tx DBTX, dep *types.Dependency, depTables []string) error {\n\tif dep.IssueID == dep.DependsOnID {\n\t\treturn fmt.Errorf(\"%w: %s cannot depend on itself\", domain.ErrSelfDependency, dep.IssueID)\n\t}\n\tif !types.IsSchedulingEdge(dep.Type) {\n\t\treturn nil\n\t}\n\twouldCycle, err := WouldCreateSchedulingCycleInTx(ctx, tx, dep.IssueID, dep.DependsOnID, depTables)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to check for dependency cycle: %w\", err)\n\t}\n\tif wouldCycle {\n\t\treturn domain.ErrDependencyCycle\n\t}\n\treturn nil\n}\n\n// WouldCreateSchedulingCycleInTx reports whether adding issueID -> dependsOnID\n// would close a cycle in the combined scheduling graph. It is shared by the\n// classic and domain storage stacks so both traverse the same dependency types\n// and typed target columns.","sourceCodeStart":425,"sourceCodeEnd":461,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependencies.go#L425-L461","documentation":"Returned when a dependency's issue and target are the same ID: an issue cannot depend on itself. This is a deliberate domain validation (wrapped around domain.ErrSelfDependency) so callers can use errors.Is to detect it, raised by CheckDependencyCycleInTx before the edge is inserted.","triggerScenarios":"Calling CheckDependencyCycleInTx (directly or via addDependencyInTx / PersistDependenciesWithOptionsResult) with a Dependency where IssueID == DependsOnID, e.g. a parent patch assigning an issue as its own parent, or a client echoing back an ID into both fields.","commonSituations":"Off-by-one or copy-paste bug in CLI/API payloads; syncing tools that map an ID to the wrong field; parent-patch applied to the issue itself.","solutions":["Compare dep.IssueID and dep.DependsOnID before calling and reject equal IDs with a clear client-side message","Fix the caller that is populating both fields with the same value","Use errors.Is(err, domain.ErrSelfDependency) to detect this case and skip the edge instead of failing the batch"],"exampleFix":"// before\nif err := issueops.CheckDependencyCycleInTx(ctx, tx, dep, nil); err != nil {\n\treturn err\n}\n// after\nif dep.IssueID == dep.DependsOnID {\n\treturn fmt.Errorf(\"skip self dependency %s\", dep.IssueID)\n}\nif err := issueops.CheckDependencyCycleInTx(ctx, tx, dep, nil); err != nil {\n\treturn err\n}","handlingStrategy":"validation","validationCode":"func validateNoSelfDep(dep types.Dependency) error {\n\tif dep.IssueID == dep.DependsOnID {\n\t\treturn fmt.Errorf(\"issue %s cannot depend on itself\", dep.IssueID)\n\t}\n\treturn nil\n}","typeGuard":"func isSelfDependency(err error) bool {\n\treturn errors.Is(err, domain.ErrSelfDependency)\n}","tryCatchPattern":"if err := issueops.CheckDependencyCycleInTx(ctx, tx, dep, nil); err != nil {\n\tif errors.Is(err, domain.ErrSelfDependency) {\n\t\treturn ErrSkipSelfDep // handle specifically, don't fail whole batch\n\t}\n\treturn err\n}","preventionTips":["Reject IssueID == DependsOnID at the API boundary before persisting","Sanitize client-supplied parent/dep IDs against echoing the subject ID","Use errors.Is(err, domain.ErrSelfDependency) for precise detection","Add unit coverage for self-dep payloads in import/sync paths"],"tags":["validation","dependency-graph","self-reference"],"backgroundTag":"self-dependency","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}