juanfont/headscale · error
serializing merged tags for node %d: %w
Error message
serializing merged tags for node %d: %w
What it means
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.
Source
Thrown at hscontrol/db/db.go:681
if len(validatedTags) == 0 {
if len(rejectedTags) > 0 {
log.Debug().
EmbedObject(node).
Strs("rejected_tags", rejectedTags).
Msg("RequestTags rejected during migration (not authorized)")
}
continue
}
mergedTags := append(slices.Clone(existingTags), validatedTags...)
slices.Sort(mergedTags)
mergedTags = slices.Compact(mergedTags)
tagsJSON, err := json.Marshal(mergedTags)
if err != nil {
return fmt.Errorf("serializing merged tags for node %d: %w", node.ID, err)
}
err = tx.Exec("UPDATE nodes SET tags = ? WHERE id = ?", string(tagsJSON), node.ID).Error
if err != nil {
return fmt.Errorf("updating tags for node %d: %w", node.ID, err)
}
log.Info().
EmbedObject(node).
Strs("validated_tags", validatedTags).
Strs("rejected_tags", rejectedTags).
Strs("existing_tags", existingTags).
Strs("merged_tags", mergedTags).
Msg("Migrated validated RequestTags from host_info to tags column")
}
return nil
},View on GitHub (pinned to 565fd254d0)
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
Defensive patterns
Strategy: try-catch
Validate before calling
// Sanity-check host_info JSON before upgrading so serialization inputs are well-formed
rows, _ := db.Query("SELECT id, host_info FROM nodes WHERE host_info IS NOT NULL AND host_info != ''")
for rows.Next() {
var id int; var raw string
rows.Scan(&id, &raw)
if !json.Valid([]byte(raw)) {
log.Warnf("node %d has malformed host_info; repair before upgrade", id)
}
} Try / catch
// This path is near-unreachable; catch, log the node ID, and skip rather than abort
if err := json.Marshal(mergedTags); err != nil {
log.Warn().Err(err).Int("node_id", node.ID).Msg("skipping tags merge for node")
continue
} Prevention
- Watch for OOM events during large migrations; raise limits
- Validate stored JSON blobs (host_info) periodically as part of DB hygiene
When it happens
Trigger: 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.
Common situations: Containers hitting cgroup memory limits during large migrations, where allocations fail in unusual places.
Related errors
- saving approved routes to new column: %w
- loading nodes for RequestTags migration: %w
- foreign key constraints violated
- version check: %w
- automigrating types.Route: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/5bdb14ff1321be50.
Report an issue: GitHub.