cilium/cilium · error

AddrCluster.UnmarshalJSON: bad address

Error message

AddrCluster.UnmarshalJSON: bad address

What it means

AddrCluster is Cilium's cluster-aware address type holding an IP plus optional ClusterID, serialized as "<addr>@<clusterID>" (e.g. "1.2.3.4@1"). UnmarshalJSON returns errUnmarshalBadAddress when the JSON value is not a quoted string of the expected form — e.g. unquoted input, empty string content, or missing surrounding quotes. The package then delegates to ParseAddrCluster, which yields a more specific parse error for malformed IP/clusterID content.

Source

Thrown at pkg/clustermesh/types/addressing.go:48

// (e.g. network endpoint has a unique IP address). We can consider
// this as a special case that ClusterID "doesn't matter". ClusterID
// 0 is reserved for indicating that.
//

// AddrCluster is a type that holds a pair of IP and ClusterID.
// We should use this type as much as possible when we implement
// IP + Cluster addressing. We should avoid managing IP and ClusterID
// separately. Otherwise, it is very hard for code readers to see
// where we are using cluster-aware addressing.
type AddrCluster struct {
	addr      netip.Addr
	clusterID uint32
}

const AddrClusterLen = 20

var (
	errUnmarshalBadAddress   = errors.New("AddrCluster.UnmarshalJSON: bad address")
	errMarshalInvalidAddress = errors.New("AddrCluster.MarshalJSON: invalid address")

	jsonZeroAddress = []byte("\"\"")
)

// MarshalJSON marshals the address as a string in the form
// <addr>@<clusterID>, e.g. "1.2.3.4@1"
func (a *AddrCluster) MarshalJSON() ([]byte, error) {
	if !a.addr.IsValid() {
		if a.clusterID != 0 {
			return nil, errMarshalInvalidAddress
		}

		// AddrCluster{} is the zero value. Preserve this across the
		// marshalling by returning an empty string.
		return jsonZeroAddress, nil
	}

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Wrap the address value in double quotes in the JSON, in the form "<ip>@<clusterID>" or a bare quoted IP like "1.2.3.4"
  2. Ensure the value is not the empty string "" — omit the field or use the zero address representation "" only for AddrCluster{}
  3. Validate with types.ParseAddrCluster(s) before marshaling the JSON to get a precise parse error
  4. Check the JSON producer: a templating/script step may be dropping the quotes

Example fix

// before
{"addresses": [1.2.3.4@1]}
// after
{"addresses": ["1.2.3.4@1"]}
Defensive patterns

Strategy: validation

Validate before calling

func validAddrClusterJSON(b []byte) bool {
	return len(b) > 2 && b[0] == '"' && b[len(b)-1] == '"'
}
// or pre-validate the string form:
if _, err := types.ParseAddrCluster(s); err != nil { /* fix before encoding */ }

Type guard

func isJSONString(data []byte) bool {
	return len(data) >= 2 && data[0] == '"' && data[len(data)-1] == '"'
}

Try / catch

var ac types.AddrCluster
if err := json.Unmarshal(data, &ac); err != nil {
	if errors.Is(err, types.ErrUnmarshalBadAddress) { // or strings.Contains
		log.Warnf("skipping bad address payload %q", data)
		return nil // or surface a remediation hint
	}
	return err
}

Prevention

When it happens

Trigger: Calling json.Unmarshal (directly or via a struct containing an AddrCluster field) with a JSON value that is not a double-quoted string: a number, an object, or a bare token; or a quoted string of length 0 (""). Strings with content go to ParseAddrCluster, so only empty/non-string JSON triggers this exact error.

Common situations: Hand-editing cluster config/StateStore JSON where an address was written unquoted (e.g. 1.2.3.4@1 without quotes); tools emitting a number instead of a string for an IP field; passing an empty string "" for an address that should have been omitted.

Related errors


AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31). Data as JSON: /api/errors/76cc29d05414237c. Report an issue: GitHub.