{"record":{"id":"37eeae96227fda00","repo":"gastownhall/beads","slug":"metadata-ref-q-references-unknown-key-q","errorCode":null,"errorMessage":"metadata_ref %q references unknown key %q","messagePattern":"metadata_ref %q references unknown key %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/types/types.go","lineNumber":1474,"sourceCode":"\t}\n\treturn NewGraphEdgeDependency(issueID, targetID, depType, \"\", \"\", \"\", \"\", nil)\n}\n\n// MergeMetadataRefs merges resolved metadata_refs into an issue's existing\n// metadata JSON: each refs entry maps a metadata key to a plan-local node\n// key, which is replaced with its minted ID from keyToID. Shared by the\n// embedded and domain graph-apply paths.\nfunc MergeMetadataRefs(existing json.RawMessage, refs map[string]string, keyToID map[string]string) (json.RawMessage, error) {\n\tmerged := make(map[string]json.RawMessage, len(refs))\n\tif len(existing) > 0 {\n\t\tif err := json.Unmarshal(existing, &merged); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"re-parsing metadata: %w\", err)\n\t\t}\n\t}\n\tfor metaKey, refKey := range refs {\n\t\tresolvedID, ok := keyToID[refKey]\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"metadata_ref %q references unknown key %q\", metaKey, refKey)\n\t\t}\n\t\tidJSON, err := json.Marshal(resolvedID)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"marshaling metadata ref %q: %w\", metaKey, err)\n\t\t}\n\t\tmerged[metaKey] = idJSON\n\t}\n\treturn json.Marshal(merged)\n}\n\n// ParseWaitsForGateMetadata extracts the waits-for gate type from dependency metadata.\n// Note: spawner identity comes from dependencies.depends_on_id in storage/query paths;\n// metadata.spawner_id is parsed for compatibility/future explicit targeting.\n// Returns WaitsForAllChildren on empty/invalid metadata for backward compatibility.\nfunc ParseWaitsForGateMetadata(metadata string) string {\n\tif strings.TrimSpace(metadata) == \"\" {\n\t\treturn WaitsForAllChildren\n\t}","sourceCodeStart":1456,"sourceCodeEnd":1492,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/types/types.go#L1456-L1492","documentation":"Each entry in refs maps a metadata key to a plan-local node key that must be translated to its minted issue ID via keyToID. If the referenced key is not in keyToID, MergeMetadataRefs fails with this error naming both the metadata key and the unknown reference key.","triggerScenarios":"Calling MergeMetadataRefs where some refs[metaKey] value is not a key present in the keyToID map.","commonSituations":"A graph plan's metadata_refs section points at a node key that was renamed, deleted, or never declared; applying a truncated or partially-generated plan.","solutions":["Add/fix the referenced node in the plan so its key exists in keyToID","Correct the metadata_refs entry to point at an existing plan node key","Regenerate the plan so refs and nodes stay in sync"],"exampleFix":"// before\n\"metadata_refs\": {\"spawner\": \"node-7\"} // node-7 not in plan\n// after\n\"metadata_refs\": {\"spawner\": \"node-3\"} // node-3 exists as a plan node","handlingStrategy":"validation","validationCode":"func validateMetadataRefs(refs map[string]string, keyToID map[string]string) error {\n\tfor metaKey, refKey := range refs {\n\t\tif _, ok := keyToID[refKey]; !ok {\n\t\t\treturn fmt.Errorf(\"metadata_refs[%q] points at unknown plan key %q\", metaKey, refKey)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":"func refsResolvable(refs, keyToID map[string]string) bool {\n\tfor _, refKey := range refs {\n\t\tif _, ok := keyToID[refKey]; !ok { return false }\n\t}\n\treturn true\n}","tryCatchPattern":"merged, err := types.MergeMetadataRefs(existing, refs, keyToID)\nif err != nil {\n\tif strings.Contains(err.Error(), \"references unknown key\") {\n\t\treturn fmt.Errorf(\"plan validation failed: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Generate metadata_refs from the same node list that builds keyToID","Validate all refs against node keys during plan load, before minting IDs","Regenerate plans wholesale after renaming or deleting nodes"],"tags":["go","graph-plan","metadata-refs","key-resolution"],"backgroundTag":"unresolved-plan-node-key","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}