{"record":{"id":"f1ce6e34b323e79d","repo":"gastownhall/beads","slug":"node-q-w","errorCode":null,"errorMessage":"node %q: %w","messagePattern":"node %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/graph_apply.go","lineNumber":653,"sourceCode":"\t\t\t\treturn fmt.Errorf(\"edge %d: spawner_id %q must match to_id %q (the waits-for target is the spawner)\", i, edge.SpawnerID, edge.ToID)\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := validateGraphApplyLocalCycles(plan, seenKeys); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// validateGraphApplyNodeFields checks the single-node fields added for\n// bd-create parity, mirroring the flag-shape checks `bd create` applies\n// (config-gated template linting is not run on graph plans).\nfunc validateGraphApplyNodeFields(node GraphApplyNode, customTypes, customStatuses []string, opts GraphApplyOptions) error {\n\tif node.ID != \"\" {\n\t\tif _, err := validation.ValidateIDFormat(node.ID); err != nil {\n\t\t\treturn fmt.Errorf(\"node %q: %w\", node.Key, err)\n\t\t}\n\t}\n\t// Friendlier status message than the issue-model validator's below.\n\tif node.Status != \"\" && !types.Status(node.Status).IsValidWithCustom(customStatuses) {\n\t\treturn fmt.Errorf(\"node %q: invalid status %q (valid: %s; configure custom statuses via 'bd config set status.custom')\", node.Key, node.Status, workapi.ValidStatusList(customStatuses))\n\t}\n\tif node.WispType != \"\" && !types.WispType(node.WispType).IsValid() {\n\t\treturn fmt.Errorf(\"node %q: invalid wisp_type %q (must be %s)\", node.Key, node.WispType, types.ValidWispTypeNames())\n\t}\n\tif node.MolType != \"\" && !types.MolType(node.MolType).IsValid() {\n\t\treturn fmt.Errorf(\"node %q: invalid mol_type %q (must be %s)\", node.Key, node.MolType, types.ValidMolTypeNames())\n\t}\n\tif (node.EventKind != \"\" || node.Actor != \"\" || node.Target != \"\" || node.Payload != \"\") && node.Type != string(types.TypeEvent) {\n\t\treturn fmt.Errorf(\"node %q: event_kind, actor, target, and payload require type %q\", node.Key, types.TypeEvent)\n\t}\n\t// Issue-model rules (type validity, priority range, estimate sign,\n\t// metadata JSON, ...) run against the same materialized issue the apply\n\t// path stores, so plan-time and insert-time validation can't drift.","sourceCodeStart":635,"sourceCodeEnd":671,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/graph_apply.go#L635-L671","documentation":"validateGraphApplyNodeFields wraps errors from validating a node's explicit ID with the node's plan key: when node.ID is set, it must pass ValidateIDFormat, and any format violation is reported as `node \"<key>\": <underlying reason>`. The wrap keeps the plan key visible even though the failing value is the ID.","triggerScenarios":"A graph plan node sets an id field that violates ID format rules (bad characters, wrong length, invalid prefix/shape), e.g. id: \"my issue!\" or an ID containing uppercase/spaces.","commonSituations":"Hand-written plans using human-readable labels as IDs; generating IDs from names without sanitization; copy-pasting IDs from external systems with different formats.","solutions":["Read the wrapped underlying message and fix node.id to satisfy the ID format rules","Remove node.id and let bd assign/resolve the ID when possible","Sanitize generated IDs (lowercase, allowed charset, separators) in your plan generator"],"exampleFix":"// before\n{\"key\": \"auth\", \"id\": \"Auth Feature #1\"}\n// after\n{\"key\": \"auth\", \"id\": \"auth-feature-1\"}","handlingStrategy":"validation","validationCode":"for _, n := range plan.Nodes {\n  if n.ID != \"\" {\n    if err := validation.ValidateIDFormat(n.ID); err != nil { return fmt.Errorf(\"node %q: %w\", n.Key, err) }\n  }\n}","typeGuard":"func validNodeID(n Node) bool { return n.ID == \"\" || validation.ValidateIDFormat(n.ID) == nil }","tryCatchPattern":"if err := bd.GraphApply(ctx, plan); err != nil {\n  var fmtErr *fmt.Errorf\n  if strings.Contains(err.Error(), \"invalid\") { /* fix plan and retry */ }\n  return err\n}","preventionTips":["Sanitize generated IDs (lowercase alphanumerics and hyphens only)","Let bd assign IDs instead of hand-writing them when possible","Lint node id fields with the same validator bd uses before apply"],"tags":["graph-plan","validation","id-format"],"backgroundTag":"invalid-id-format","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}