slackhq/nebula · error

nil byte array

Error message

nil byte array

What it means

unmarshalCertificateV1 refuses to decode an empty protobuf byte slice; there is nothing to unmarshal so it returns 'nil byte array' immediately. This fails fast before attempting protobuf parsing so callers get a clear message instead of a generic proto error.

Source

Thrown at cert/cert_v1.go:404

	if err != nil {
		return nil, err
	}
	return b, nil
}

func (c *certificateV1) setSignature(b []byte) error {
	if len(b) == 0 {
		return ErrEmptySignature
	}
	c.signature = b
	return nil
}

// unmarshalCertificateV1 will unmarshal a protobuf byte representation of a nebula cert
// if the publicKey is provided here then it is not required to be present in `b`
func unmarshalCertificateV1(b []byte, publicKey []byte) (*certificateV1, error) {
	if len(b) == 0 {
		return nil, fmt.Errorf("nil byte array")
	}
	var rc RawNebulaCertificate
	err := proto.Unmarshal(b, &rc)
	if err != nil {
		return nil, err
	}

	if rc.Details == nil {
		return nil, fmt.Errorf("encoded Details was nil")
	}

	if len(rc.Details.Ips)%2 != 0 {
		return nil, fmt.Errorf("encoded IPs should be in pairs, an odd number was found")
	}

	if len(rc.Details.Subnets)%2 != 0 {
		return nil, fmt.Errorf("encoded Subnets should be in pairs, an odd number was found")
	}

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Check len(b) > 0 before calling unmarshalCertificateV1 and surface a caller-appropriate error
  2. Fix the upstream read (file, network, KV) that produced empty bytes; check read errors
  3. Re-obtain or re-issue the certificate if the source data is genuinely empty
  4. Add logging of the byte-slice source path/key to find why it is empty

Example fix

// before
if len(rawCert) == 0 {
    // silently calls unmarshal, gets 'nil byte array'
}
c, err := unmarshalCertificateV1(rawCert, nil)
// after
if len(rawCert) == 0 {
    return nil, fmt.Errorf("certificate data from %s is empty", certPath)
}
c, err := unmarshalCertificateV1(rawCert, nil)
Defensive patterns

Strategy: validation

Validate before calling

if len(rawCertBytes) == 0 {
    return nil, fmt.Errorf("certificate source %s produced no bytes", source)
}
c, err := unmarshalCertificateV1(rawCertBytes, nil)

Type guard

func hasCertBytes(b []byte) bool { return len(b) > 0 }

Try / catch

c, err := unmarshalCertificateV1(rawCertBytes, nil)
if err != nil {
    if strings.Contains(err.Error(), "nil byte array") {
        return nil, fmt.Errorf("certificate data is empty; check the source read")
    }
    return nil, err
}

Prevention

When it happens

Trigger: Passing an empty (len==0) byte slice to unmarshalCertificateV1 via Recombine, unmarshalCertificateBlock, or directly; e.g. a zero-byte certificate file, an empty protobuf field, or a helper returning no bytes.

Common situations: Certificate file truncated to zero bytes on disk; failed read treated as success; empty value pulled from a key-value store; a nil/empty raw certificate field in a bundle being recombined.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/d23a0ae3c812896d. Report an issue: GitHub.