{"record":{"id":"7477c0e8781bb33a","repo":"gastownhall/beads","slug":"serializing-waits-for-metadata-w-7477c0","errorCode":null,"errorMessage":"serializing waits-for metadata: %w","messagePattern":"serializing waits-for metadata: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/types/types.go","lineNumber":1398,"sourceCode":"\t\tIssueID:     fromID,\n\t\tDependsOnID: toID,\n\t\tType:        depType,\n\t\tThreadID:    threadID,\n\t}\n\tif depType == DepWaitsFor {\n\t\tif spawnerKey != \"\" {\n\t\t\tresolved, ok := keyToID[spawnerKey]\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"serializing waits-for metadata: unresolved spawner key %q\", spawnerKey)\n\t\t\t}\n\t\t\tspawnerID = resolved\n\t\t}\n\t\tif gate == \"\" {\n\t\t\tgate = WaitsForAllChildren\n\t\t}\n\t\traw, err := json.Marshal(WaitsForMeta{Gate: gate, SpawnerID: spawnerID})\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"serializing waits-for metadata: %w\", err)\n\t\t}\n\t\tdep.Metadata = string(raw)\n\t}\n\treturn dep, nil\n}\n\n// NewWaitsForDependency builds the waits-for dependency record for a single\n// issue outside a graph plan: the spawner is the depends_on target and the\n// metadata carries the gate (defaulted to all-children). Shares\n// NewGraphEdgeDependency so single-issue and graph-created waits-for rows\n// cannot drift.\nfunc NewWaitsForDependency(issueID, spawnerID, gate string) (*Dependency, error) {\n\treturn NewGraphEdgeDependency(issueID, spawnerID, DepWaitsFor, gate, \"\", \"\", \"\", nil)\n}\n\n// NewWaitsForBlockingDependency builds a waits-for dependency that also\n// carries classic blocking semantics (GH#3783): set also_blocks in the\n// metadata so waitsForGateBlockedSQL additionally blocks while the spawner","sourceCodeStart":1380,"sourceCodeEnd":1416,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/types/types.go#L1380-L1416","documentation":"NewGraphEdgeDependency serializes the WaitsForMeta struct (gate + spawnerID) into the dependency's metadata JSON for waits-for edges. If json.Marshal fails — practically impossible for this fixed-structure string struct — the error is wrapped with this message.","triggerScenarios":"json.Marshal(WaitsForMeta{...}) returning a non-nil error inside NewGraphEdgeDependency for a DepWaitsFor edge.","commonSituations":"Essentially unreachable in practice since WaitsForMeta contains only string fields; would indicate a corrupted build or a custom JSON marshaler on the type.","solutions":["Rebuild the binary / clear build cache to rule out a stale or patched types package","If it persists, check for any custom MarshalJSON override on WaitsForMeta and remove it","Report upstream with the wrapped inner error, since stock WaitsForMeta cannot fail to marshal"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// WaitsForMeta is a plain string struct; nothing meaningful to pre-validate.\n// Optionally assert the build is stock:\n// if !json.Valid([]byte(`{\"gate\":\"all-children\",\"spawner_id\":\"\"}`)) { /* non-standard json pkg */ }","typeGuard":null,"tryCatchPattern":"dep, err := types.NewGraphEdgeDependency(fromID, toID, depType, gate, spawnerKey, spawnerID, threadID, keyToID)\nif err != nil {\n\tif strings.Contains(err.Error(), \"serializing waits-for metadata\") {\n\t\tlog.Fatalf(\"waits-for metadata serialization failed: %v\", err)\n\t}\n\treturn err\n}","preventionTips":["Do not add custom MarshalJSON to WaitsForMeta","Rebuild from a clean checkout if this ever fires","Include the wrapped inner error in bug reports"],"tags":["go","json","serialization","waits-for"],"backgroundTag":"json-marshal-failure","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}