FiloSottile/age · error

invalid tag recipient public key: %v

Error message

invalid tag recipient public key: %v

What it means

After the length check, NewClassicRecipient decodes the bytes as a compressed P-256 point via nistec.NewP256Point().SetBytes. This error reports a point-decoding failure: the bytes have the right size but do not represent a point on the P-256 curve (bad prefix byte or invalid coordinates).

Source

Thrown at tag/tag.go:76

			return nil, fmt.Errorf("malformed recipient %q: %v", s, err)
		}
		return r, nil
	default:
		return nil, fmt.Errorf("malformed recipient %q: invalid type %q", s, t)
	}
}

const compressedPointSize = 1 + 32
const uncompressedPointSize = 1 + 32 + 32

// NewClassicRecipient returns a new P-256 [Recipient] from a raw public key.
func NewClassicRecipient(publicKey []byte) (*Recipient, error) {
	if len(publicKey) != compressedPointSize {
		return nil, fmt.Errorf("invalid tag recipient public key size %d", len(publicKey))
	}
	p, err := nistec.NewP256Point().SetBytes(publicKey)
	if err != nil {
		return nil, fmt.Errorf("invalid tag recipient public key: %v", err)
	}
	k, err := hpke.DHKEM(ecdh.P256()).NewPublicKey(p.Bytes())
	if err != nil {
		return nil, fmt.Errorf("invalid tag recipient public key: %v", err)
	}
	return &Recipient{k}, nil
}

// NewHybridRecipient returns a new hybrid P-256 + ML-KEM-768 [Recipient] from
// raw concatenated public keys.
func NewHybridRecipient(publicKey []byte) (*Recipient, error) {
	k, err := hpke.MLKEM768P256().NewPublicKey(publicKey)
	if err != nil {
		return nil, fmt.Errorf("invalid tagpq recipient public key: %v", err)
	}
	return &Recipient{k}, nil
}

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Check the wrapped %v for the exact SetBytes failure.
  2. Verify the key really is a P-256 (NIST) public key, not X25519/Ed25519 or another curve.
  3. Ensure the first byte is the SEC1 compressed prefix 0x02 or 0x03.
  4. Re-export the key from its source in compressed SEC1 form rather than hand-assembling bytes.
Defensive patterns

Strategy: validation

Validate before calling

if len(publicKey) == 33 && (publicKey[0] == 0x02 || publicKey[0] == 0x03) {
    if _, err := tag.NewClassicRecipient(publicKey); err != nil {
        return fmt.Errorf("not a valid P-256 point: %w", err)
    }
}

Type guard

func isCompressedP256Point(b []byte) bool {
    if len(b) != 33 || (b[0] != 0x02 && b[0] != 0x03) {
        return false
    }
    _, err := nistec.NewP256Point().SetBytes(b)
    return err == nil
}

Prevention

When it happens

Trigger: tag.NewClassicRecipient with a 33-byte slice whose first byte is not 0x02/0x03, or whose x-coordinate has no matching y on the curve.

Common situations: Random/corrupted bytes passed as a key; endian or format confusion (e.g. Ed25519 or Curve25519 keys fed as P-256); manually assembled key material.

Related errors


AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31). Data as JSON: /api/errors/d5a770312942cae2. Report an issue: GitHub.