{"record":{"id":"5bdb14ff1321be50","repo":"juanfont/headscale","slug":"serializing-merged-tags-for-node-d-w","errorCode":null,"errorMessage":"serializing merged tags for node %d: %w","messagePattern":"serializing merged tags for node (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/db/db.go","lineNumber":681,"sourceCode":"\n\t\t\t\t\t\tif len(validatedTags) == 0 {\n\t\t\t\t\t\t\tif len(rejectedTags) > 0 {\n\t\t\t\t\t\t\t\tlog.Debug().\n\t\t\t\t\t\t\t\t\tEmbedObject(node).\n\t\t\t\t\t\t\t\t\tStrs(\"rejected_tags\", rejectedTags).\n\t\t\t\t\t\t\t\t\tMsg(\"RequestTags rejected during migration (not authorized)\")\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmergedTags := append(slices.Clone(existingTags), validatedTags...)\n\t\t\t\t\t\tslices.Sort(mergedTags)\n\t\t\t\t\t\tmergedTags = slices.Compact(mergedTags)\n\n\t\t\t\t\t\ttagsJSON, err := json.Marshal(mergedTags)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn fmt.Errorf(\"serializing merged tags for node %d: %w\", node.ID, err)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\terr = tx.Exec(\"UPDATE nodes SET tags = ? WHERE id = ?\", string(tagsJSON), node.ID).Error\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn fmt.Errorf(\"updating tags for node %d: %w\", node.ID, err)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlog.Info().\n\t\t\t\t\t\t\tEmbedObject(node).\n\t\t\t\t\t\t\tStrs(\"validated_tags\", validatedTags).\n\t\t\t\t\t\t\tStrs(\"rejected_tags\", rejectedTags).\n\t\t\t\t\t\t\tStrs(\"existing_tags\", existingTags).\n\t\t\t\t\t\t\tStrs(\"merged_tags\", mergedTags).\n\t\t\t\t\t\t\tMsg(\"Migrated validated RequestTags from host_info to tags column\")\n\t\t\t\t\t}\n\n\t\t\t\t\treturn nil\n\t\t\t\t},","sourceCodeStart":663,"sourceCodeEnd":699,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/db/db.go#L663-L699","documentation":"The RequestTags migration fails json.Marshal-ing the merged tag list for a node. Marshal of a []string of tag names essentially cannot fail (no channels/funcs/cycles in scope), so seeing this error in practice signals memory corruption, an OOM-adjacent allocation failure, or malformed data reaching the slice from unparsed host_info. It is the least likely error in this migration.","triggerScenarios":"mergedTags containing a value json.Marshal rejects; in practice a failed allocation on an out-of-memory host rather than any realistic tag value, since validatedTags and existingTags are []string.","commonSituations":"Containers hitting cgroup memory limits during large migrations, where allocations fail in unusual places.","solutions":["Check dmesg/container logs for OOM kills; raise memory limits and retry the upgrade","Capture the node ID from the message, inspect its host_info and tags columns for malformed JSON, and repair that row manually","If genuinely reproducible, report upstream with the node's host_info contents - this code path should be unreachable for valid []string data"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Sanity-check host_info JSON before upgrading so serialization inputs are well-formed\nrows, _ := db.Query(\"SELECT id, host_info FROM nodes WHERE host_info IS NOT NULL AND host_info != ''\")\nfor rows.Next() {\n\tvar id int; var raw string\n\trows.Scan(&id, &raw)\n\tif !json.Valid([]byte(raw)) {\n\t\tlog.Warnf(\"node %d has malformed host_info; repair before upgrade\", id)\n\t}\n}","typeGuard":null,"tryCatchPattern":"// This path is near-unreachable; catch, log the node ID, and skip rather than abort\nif err := json.Marshal(mergedTags); err != nil {\n\tlog.Warn().Err(err).Int(\"node_id\", node.ID).Msg(\"skipping tags merge for node\")\n\tcontinue\n}","preventionTips":["Watch for OOM events during large migrations; raise limits","Validate stored JSON blobs (host_info) periodically as part of DB hygiene"],"tags":["database","migration","json","memory","unreachable-path"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}