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

  1. Check dmesg/container logs for OOM kills; raise memory limits and retry the upgrade
  2. Capture the node ID from the message, inspect its host_info and tags columns for malformed JSON, and repair that row manually
  3. 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

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


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/5bdb14ff1321be50. Report an issue: GitHub.