router-for-me/CLIProxyAPI · error

private_key pem decode failed

Error message

private_key pem decode failed

What it means

Internal guard in sanitizePrivateKey: after either the original input or the reconstructed PEM from rebuildPEM, a second pem.Decode still returns a nil block (keyutil.go:73-76). Practically this indicates the reconstruction produced text that pem.Decode rejects (e.g. bad header formatting), so the key cannot be trusted.

Source

Thrown at internal/auth/vertex/keyutil.go:76

	pk := strings.ReplaceAll(raw, "\r\n", "\n")
	pk = strings.ReplaceAll(pk, "\r", "\n")
	pk = stripANSIEscape(pk)
	pk = strings.ToValidUTF8(pk, "")
	pk = strings.TrimSpace(pk)

	normalized := pk
	if block, _ := pem.Decode([]byte(pk)); block == nil {
		// Attempt to reconstruct from the textual payload.
		if reconstructed, err := rebuildPEM(pk); err == nil {
			normalized = reconstructed
		} else {
			return "", fmt.Errorf("private_key is not valid pem: %w", err)
		}
	}

	block, _ := pem.Decode([]byte(normalized))
	if block == nil {
		return "", fmt.Errorf("private_key pem decode failed")
	}

	rsaBlock, err := ensureRSAPrivateKey(block)
	if err != nil {
		return "", err
	}
	return string(pem.EncodeToMemory(rsaBlock)), nil
}

func ensureRSAPrivateKey(block *pem.Block) (*pem.Block, error) {
	if block == nil {
		return nil, fmt.Errorf("pem block is nil")
	}

	if block.Type == "RSA PRIVATE KEY" {
		if _, err := x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
			return nil, fmt.Errorf("private_key invalid rsa: %w", err)
		}

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Discard the edited key and re-download the original JSON key file from GCP unchanged
  2. Compare the PEM header/footer lines byte-for-byte with a known-good key (-----BEGIN PRIVATE KEY-----, no extra spaces)
  3. Run the key through: cat sa.json | jq -r .private_key | openssl rsa -noout -check to validate independently of this code

Example fix

# before
----- BEGIN PRIVATE KEY -----   # hand-edited spaces break pem.Decode
# after
-----BEGIN PRIVATE KEY-----
Defensive patterns

Strategy: validation

Validate before calling

if block, _ := pem.Decode([]byte(pk)); block == nil {
    return fmt.Errorf("private_key is not decodable PEM even after cleanup")
}

Prevention

When it happens

Trigger: rebuildPEM succeeds in extracting base64 and re-encoding, but the resulting block still fails Go's strict pem.Decode (mismatched header spacing, BOM characters, non-UTF8 residue); extremely rare double-failure where the raw input almost decodes but has structural damage.

Common situations: Keys processed through multiple serialization layers (YAML on/off, JSON escapes, terminal paste with ANSI codes) leaving subtle corruption; editing the PEM headers by hand ('----- BEGIN' with a space).

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/71a0ea72abbd6a98. Report an issue: GitHub.