BerriAI/litellm · critical · ValueError
Private key file is empty: {file_path}
Error message
Private key file is empty: {file_path} What it means
After successfully opening the key file, load_private_key_from_file checks that its content is non-empty (after strip) and raises ValueError naming the path if not. An empty file means the credential was never actually written — typically a truncated provisioning step.
Source
Thrown at litellm/llms/oci/common_utils.py:138
password=None,
)
if not isinstance(key, rsa.RSAPrivateKey):
raise TypeError("The provided private key is not an RSA key, which is required for OCI signing.")
return key
def load_private_key_from_file(file_path: str) -> Any:
"""Loads a private key from a file path."""
try:
with open(file_path, "r", encoding="utf-8") as f:
key_str: Final = f.read().strip()
except FileNotFoundError:
raise FileNotFoundError(f"Private key file not found: {file_path}")
except OSError as e:
raise OSError(f"Failed to read private key file '{file_path}': {e}") from e
if not key_str:
raise ValueError(f"Private key file is empty: {file_path}")
return load_private_key_from_str(key_str)
# ---------------------------------------------------------------------------
# Env-var credential resolution
# ---------------------------------------------------------------------------
_OCI_REGION_ENV: Final = "OCI_REGION"
_OCI_USER_ENV: Final = "OCI_USER"
_OCI_FINGERPRINT_ENV: Final = "OCI_FINGERPRINT"
_OCI_TENANCY_ENV: Final = "OCI_TENANCY"
_OCI_KEY_FILE_ENV: Final = "OCI_KEY_FILE"
_OCI_KEY_ENV: Final = "OCI_KEY"
_OCI_COMPARTMENT_ID_ENV: Final = "OCI_COMPARTMENT_ID"
def resolve_oci_credentials(optional_params: dict) -> dict:View on GitHub (pinned to 6c2dcb801b)
Solutions
- Inspect the file: wc -c <keyfile> and head it — expect ~1700+ bytes of PEM for RSA-2048.
- Regenerate the secret/CI step from the real PEM source and guard against empty values: test -s key.pem || exit 1.
- In CI, fail fast when the key variable is unset: : "${OCI_KEY:?OCI_KEY missing}".
- Store the PEM in a proper secret manager instead of shell redirection.
Example fix
# before (CI step that can silently write an empty file)
run: echo "$OCI_KEY" > /secrets/oci_key.pem
# after
run: |
test -n "$OCI_KEY" || { echo "OCI_KEY empty"; exit 1; }
printf '%s\n' "$OCI_KEY" > /secrets/oci_key.pem Defensive patterns
Strategy: validation
Validate before calling
import os
p = os.path.expanduser(os.environ["OCI_KEY_FILE"])
assert os.path.getsize(p) > 1000, f"key file too small ({os.path.getsize(p)} bytes) — likely truncated/empty" Try / catch
try:
litellm.completion(model="oci/...", messages=m)
except ValueError as e:
if "Private key file is empty" in str(e):
raise ProvisioningError("re-create the secret from the real PEM") from e
raise Prevention
- In CI/secret provisioning, fail when the source value is empty (test -n / test -s).
- Size-check key files during deployment validation.
- Pull keys from a real secret manager rather than shell redirection.
When it happens
Trigger: Kubernetes Secret or Docker secret created from an empty/failed command (kubectl create secret with a wrong --from-file); CI writing the key via an unset env var (> key.pem with empty $OCI_KEY); placeholder file committed with no content.
Common situations: CI pipelines doing echo "$OCI_KEY" > key.pem where the variable is empty in the CI environment; secret templates rendered before the vault lookup; an interrupted key-generation step leaving a zero-byte file; a .gitkeep-style placeholder accidentally referenced.
Related errors
- Private key file not found: {file_path}
- Missing required OCI credentials: oci_user, oci_fingerprint,
- oci_key must be a string containing the PEM private key cont
- Error: {response.status_code} - {response.text}
- Missing Authorization header
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/ef271d814c2d7d43.
Report an issue: GitHub.