{"record":{"id":"38a4346520f829a3","repo":"gastownhall/beads","slug":"applygraph-edge-d-w","errorCode":null,"errorMessage":"applyGraph: edge %d: %w","messagePattern":"applyGraph: edge (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/issue.go","lineNumber":1261,"sourceCode":"\t\t\t\tdepType = types.DepBlocks\n\t\t\t}\n\t\t\tif (depType == types.DepParentChild) != parentPhase {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif parentDepPairs[depPairKey(fromID, toID)] {\n\t\t\t\tif depType == types.DepParentChild {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: edge %d %s->%s duplicates a parent-child relationship with dependency type %q\", i, fromID, toID, depType)\n\t\t\t}\n\t\t\tif parentDepPairs[depPairKey(toID, fromID)] && cycleRelevantDepType(depType) {\n\t\t\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: edge %d %s->%s creates a blocking reverse of a parent-child relationship\", i, fromID, toID)\n\t\t\t}\n\n\t\t\tdep, err := types.NewGraphEdgeDependency(fromID, toID, depType, edge.Gate, edge.SpawnerKey, edge.SpawnerID, edge.ThreadID, keyToID)\n\t\t\tif err != nil {\n\t\t\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: edge %d: %w\", i, err)\n\t\t\t}\n\t\t\tif err := u.depRepo.Insert(ctx, dep, actor, DepInsertOpts{UseWispsTable: useWisp}); err != nil {\n\t\t\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: edge %d (%s -> %s): %w\", i, fromID, toID, err)\n\t\t\t}\n\t\t\tif types.IsSchedulingEdge(depType) {\n\t\t\t\tnewSchedulingEdges = append(newSchedulingEdges, [2]string{fromID, toID})\n\t\t\t}\n\t\t}\n\n\t\t// Per-node inline deps in stable order for this phase, resolved by the\n\t\t// same shared builder as the embedded executeGraphApply (cmd/bd/graph_apply.go).\n\t\tfor _, node := range plan.Nodes {\n\t\t\tfor _, nd := range node.Deps {\n\t\t\t\tdep, err := types.NewGraphNodeDependency(keyToID[node.Key], nd.Type, nd.Target, keyToID)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: node %q: %w\", node.Key, err)\n\t\t\t\t}\n\t\t\t\tif (dep.Type == types.DepParentChild) != parentPhase {","sourceCodeStart":1243,"sourceCodeEnd":1279,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/issue.go#L1243-L1279","documentation":"After all structural checks pass, applyGraph builds the dependency via types.NewGraphEdgeDependency (validating type/gate/spawner/thread references against keyToID) and inserts it. Any failure from the constructor is wrapped as \"applyGraph: edge %d: %w\" — this identifies the constructor stage, distinct from the storage-insert wrapper that includes the from->to IDs.","triggerScenarios":"NewGraphEdgeDependency returns an error for edge i: invalid gate spec, spawner key/id unresolvable, unsupported type/gate combination, or malformed thread reference in the plan edge.","commonSituations":"Hand-authored gate expressions with syntax errors; spawner key pointing at an issue not in the plan; a beads version mismatch where the client emits edge fields the constructor rejects; thread ID copied from another issue.","solutions":["Inspect the wrapped cause (%w) for the constructor's specific complaint and fix that edge field","Validate gate syntax client-side before building the plan","Ensure SpawnerKey/SpawnerID resolve (add a plan node or clear the field)","Upgrade/downgrade the client so edge fields match the installed beads version"],"exampleFix":"// before\nGraphEdge{FromKey: \"bd-1\", ToKey: \"bd-2\", Type: \"conditional-blocks\", Gate: \"status == open &&\"} // malformed gate\n// after\nGraphEdge{FromKey: \"bd-1\", ToKey: \"bd-2\", Type: \"conditional-blocks\", Gate: \"status == open\"}","handlingStrategy":"try-catch","validationCode":"// Validate edge extras before building the plan:\nfor i, e := range plan.Edges {\n    if e.Gate != \"\" && !validGateSyntax(e.Gate) {\n        return fmt.Errorf(\"edge %d: malformed gate %q\", i, e.Gate)\n    }\n    if e.SpawnerKey != \"\" {\n        if _, ok := keyToID[e.SpawnerKey]; !ok { return fmt.Errorf(\"edge %d: spawner %q unresolved\", i, e.SpawnerKey) }\n    }\n}","typeGuard":"func edgeConstructible(e GraphEdge, keyToID map[string]string) bool {\n    if e.Gate != \"\" && !validGateSyntax(e.Gate) { return false }\n    if e.SpawnerKey != \"\" {\n        if _, ok := keyToID[e.SpawnerKey]; !ok { return false }\n    }\n    return true\n}","tryCatchPattern":"if err := applyGraphEdges(ctx, plan); err != nil {\n    if strings.HasPrefix(err.Error(), \"applyGraph: edge \") && !strings.Contains(err.Error(), \"->\") {\n        // constructor-stage failure: fix the named edge's gate/spawner/thread fields\n        idx := parseEdgeIndex(err.Error())\n        log.Printf(\"edge %d rejected by NewGraphEdgeDependency: %v\", idx, err)\n    }\n    return err\n}","preventionTips":["Validate gate expressions and spawner references before plan submission","Keep client and beads versions aligned to avoid field-shape mismatches","Only reference threads/spawners from issues included in or present in the DB","Unit-test plan builders against NewGraphEdgeDependency's constraints"],"tags":["graph-apply","edge-validation","wrapped-error"],"backgroundTag":"invalid-graph-edge","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}