juanfont/headscale · error
%w, got: %v(%d)
Error message
%w, got: %v(%d)
What it means
ProtocolPort.UnmarshalJSON split a protocol:port string on ':' and got a part count other than 2 — too many colons or none where the string still reached this branch. ErrProtocolPortInvalidFormat reports the parts and their count so the malformed shape is obvious.
Source
Thrown at hscontrol/policy/v2/types.go:957
return nil
}
// Only contains a port, no protocol
if !strings.Contains(vs, ":") {
ports, err := parsePortRange(vs)
if err != nil {
return fmt.Errorf("port range %q: %w", vs, err)
}
ve.Protocol = ProtocolNameWildcard
ve.Ports = ports
return nil
}
parts := strings.Split(vs, ":")
if len(parts) != 2 {
return fmt.Errorf("%w, got: %v(%d)", ErrProtocolPortInvalidFormat, parts, len(parts))
}
protocol := Protocol(parts[0])
err := protocol.validate()
if err != nil {
return err
}
portsPart := parts[1]
ports, err := parsePortRange(portsPart)
if err != nil {
return fmt.Errorf("port range %q: %w", portsPart, err)
}
ve.Protocol = protocol
ve.Ports = portsView on GitHub (pinned to 565fd254d0)
Solutions
- Use exactly one colon: 'tcp:80' or a range 'tcp:80-90'.
- List additional ports as separate entries in the protocols array.
- Validate generated policy strings contain at most one colon before writing them.
Example fix
// before "protocols": ["tcp:80:443"] // after "protocols": ["tcp:80", "tcp:443"]
Defensive patterns
Strategy: validation
Validate before calling
if strings.Count(s, ":") != 1 {
return fmt.Errorf("%q must be exactly 'proto:port' (one colon)", s)
} Try / catch
if err := pp.UnmarshalJSON([]byte(s)); err != nil {
if errors.Is(err, v2.ErrProtocolPortInvalidFormat) {
// message prints parts and count; split into multiple entries
}
return err
} Prevention
- Exactly one colon per protocols entry.
- Multiple ports -> multiple entries or one range.
- Unit-test policy string builders for colon count.
When it happens
Trigger: Values like 'tcp:80:443' (two colons, three parts) or an empty/edge string that splits oddly. len(parts) != 2 after strings.Split(vs, ":").
Common situations: Trying to list multiple ports with colons ('tcp:80:443') instead of a range; IPv6 literals or URLs accidentally placed in the protocols field; double separators from concatenation bugs.
Related errors
- port range %q: %w
- dst=%q: port range %q: %w
- protocol does not support specific ports
- invalid destination %q: %w
- %w, got: %q
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/b3627da95de05065.
Report an issue: GitHub.