{"record":{"id":"08aa207c5127abde","repo":"multica-ai/multica","slug":"hermes-config-unexpected-root-node","errorCode":null,"errorMessage":"hermes config: unexpected root node","messagePattern":"hermes config: unexpected root node","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/daemon/execenv/hermes_home.go","lineNumber":833,"sourceCode":"\t}\n\tif ed.Kind != yaml.SequenceNode {\n\t\treturn nil\n\t}\n\tout := make([]string, 0, len(ed.Content))\n\tfor _, c := range ed.Content {\n\t\tif c.Kind == yaml.ScalarNode {\n\t\t\tout = append(out, c.Value)\n\t\t}\n\t}\n\treturn out\n}\n\n// setHermesExternalDirs sets skills.external_dirs on the config document,\n// creating the skills mapping if needed and preserving every other setting.\nfunc setHermesExternalDirs(doc *yaml.Node, dirs []string) error {\n\ttop := yamlDocumentRoot(doc)\n\tif top == nil {\n\t\treturn fmt.Errorf(\"hermes config: unexpected root node\")\n\t}\n\tskills := yamlMapValue(top, \"skills\")\n\tif skills == nil || skills.Kind != yaml.MappingNode {\n\t\tskills = &yaml.Node{Kind: yaml.MappingNode, Tag: \"!!map\"}\n\t\tyamlSetMapValue(top, \"skills\", skills)\n\t}\n\tyamlSetMapValue(skills, \"external_dirs\", yamlStringSeq(dirs))\n\treturn nil\n}\n\n// yamlDocumentRoot returns the top-level mapping node of a parsed document, or\n// nil if the shape isn't a mapping.\nfunc yamlDocumentRoot(doc *yaml.Node) *yaml.Node {\n\tif doc == nil {\n\t\treturn nil\n\t}\n\tnode := doc\n\tif node.Kind == yaml.DocumentNode {","sourceCodeStart":815,"sourceCodeEnd":851,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/daemon/execenv/hermes_home.go#L815-L851","documentation":"setHermesExternalDirs parsed the user's config.yaml successfully but the document's root node is not a YAML mapping (yamlDocumentRoot returned nil — e.g. the file is a top-level sequence, scalar, or empty document). skills.external_dirs can only be set on a mapping root, so the derived-config step refuses to proceed.","triggerScenarios":"A config.yaml whose top level is a list ('- a\\n- b'), a bare scalar ('hello'), or an effectively empty document reaches writeDerivedHermesConfig and the parse succeeds but yields a non-mapping root.","commonSituations":"Hand-edited or template-generated config.yaml with wrong structure; a config meant for a different tool pasted into ~/.hermes/config.yaml; truncated multi-document YAML where the first document is a flow sequence.","solutions":["Open the shared home's config.yaml and check the top level is a mapping of key: value pairs (no leading '-').","Validate with a YAML linter or `python -c \"import yaml,sys; d=yaml.safe_load(open(...)); print(type(d))\"` — it must be a dict.","Fix or replace the config with a valid Hermes config; if none is wanted, deleting config.yaml is fine (missing config is a supported path).","Rerun the task after fixing."],"exampleFix":"# before (~/.hermes/config.yaml — invalid, top-level sequence)\n- model: gpt-4\n- skills:\n    external_dirs: [/x]\n\n# after\nmodel: gpt-4\nskills:\n  external_dirs:\n    - /x","handlingStrategy":"validation","validationCode":"var doc yaml.Node\nif err := yaml.Unmarshal(data, &doc); err != nil { /* handled by verbatim-copy path */ }\nif root := yamlDocumentRoot(&doc); root == nil {\n\treturn fmt.Errorf(\"config.yaml root is not a mapping — fix %s\", srcConfig)\n}","typeGuard":"func isMappingRoot(doc *yaml.Node) bool {\n\treturn doc != nil && yamlDocumentRoot(doc) != nil\n}","tryCatchPattern":"if err := setHermesExternalDirs(&doc, dirs); err != nil {\n\tif errors.Is(err, errHermesConfigRoot) || strings.Contains(err.Error(), \"unexpected root node\") {\n\t\t// degrade gracefully: copy user config verbatim, log which file is malformed\n\t\treturn writeFileAtomic(dstConfig, data, 0o600)\n\t}\n\treturn err\n}","preventionTips":["Lint user config.yaml for a mapping root before enabling Hermes tasks.","Automated checks: `yaml.safe_load` returning a dict is the shape this code requires."],"tags":["yaml","config","validation","hermes","execenv"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}