hashicorp/terraform · error
can not read private key from: '%s', Error: %q
Error message
can not read private key from: '%s', Error: %q
What it means
Returned by PrivateRSAKey() when os.ReadFile fails to read the PEM file at private_key_path. The backend resolves the path via expandPath, attempts to read the file, and wraps the underlying I/O error. The file must contain a PEM-encoded RSA private key.
Source
Thrown at internal/backend/remote-state/oci/auth.go:147
fingerprint, err := p.KeyFingerprint()
if err != nil {
return "", err
}
return fmt.Sprintf("%s/%s/%s", tenancy, user, fingerprint), nil
}
func (p ociAuthConfigProvider) PrivateRSAKey() (key *rsa.PrivateKey, err error) {
if p.privateKey != "" {
keyData := strings.ReplaceAll(p.privateKey, "\\n", "\n") // Ensure \n is replaced by actual newlines
return common.PrivateKeyFromBytesWithPassword([]byte(keyData), []byte(p.privateKeyPassword))
}
if p.privateKeyPath != "" {
resolvedPath := expandPath(p.privateKeyPath)
pemFileContent, readFileErr := os.ReadFile(resolvedPath)
if readFileErr != nil {
return nil, fmt.Errorf("can not read private key from: '%s', Error: %q", p.privateKeyPath, readFileErr)
}
return common.PrivateKeyFromBytesWithPassword(pemFileContent, []byte(p.privateKeyPassword))
}
return nil, fmt.Errorf("can not get private_key or private_key_path from Terraform configuration")
}
func (p ociAuthConfigProvider) getConfigProviders() ([]common.ConfigurationProvider, error) {
var configProviders []common.ConfigurationProvider
logger := logWithOperation("AuthConfigProvider")
logger.Debug(fmt.Sprintf("Using %s authentication", p.authType))
switch strings.ToLower(p.authType) {
case strings.ToLower(AuthAPIKeySetting):
// No additional config providers needed
case strings.ToLower(AuthInstancePrincipalSetting):
logger.Info("Attempting to authenticate using instance principal credentials")
if p.region == "" {View on GitHub (pinned to d32a084675)
Solutions
- Use an absolute path for private_key_path to avoid working-directory ambiguity.
- Verify the file exists and is readable: ls -la /full/path/to/key.pem; chmod 600 if needed.
- If running in a container or CI, ensure the key file is mounted/copied into the environment and the path matches.
- Switch to the inline 'private_key' attribute (with \n-escaped PEM) if file access is unreliable in the environment.
- Check that expandPath resolves ~ correctly — if unsure, replace ~ with the full home directory path.
Example fix
// before
backend "oci" {
private_key_path = "~/keys/oci_key.pem"
}
// after
backend "oci" {
private_key_path = "/home/user/keys/oci_key.pem"
}
// or inline:
backend "oci" {
private_key = "-----BEGIN RSA PRIVATE KEY-----\nMIIE...\n-----END RSA PRIVATE KEY-----"
} Defensive patterns
Strategy: validation
Validate before calling
func validatePrivateKeyPath(path string) error {
if path == "" {
return nil // handled by other validation
}
abs, err := filepath.Abs(expandPath(path))
if err != nil {
return fmt.Errorf("cannot resolve private_key_path: %w", err)
}
info, err := os.Stat(abs)
if err != nil {
return fmt.Errorf("private_key_path not accessible: %w", err)
}
if info.IsDir() {
return fmt.Errorf("private_key_path is a directory, not a file")
}
if info.Mode().Perm()&0400 == 0 {
return fmt.Errorf("private_key_path is not readable")
}
return nil
} Try / catch
// Validate key file before terraform init:
if err := validatePrivateKeyPath(cfg.PrivateKeyPath); err != nil {
log.Fatal(err)
} Prevention
- Always use absolute paths for private_key_path in backend config.
- Verify file existence and permissions in a pre-init step.
- In CI, inject the key as a file via secrets manager and validate it before running terraform.
When it happens
Trigger: Backend block has 'private_key_path' set but the file does not exist, is not readable (permission denied), or the path is wrong (relative path resolves to an unexpected directory, ~ not expanded, wrong working directory).
Common situations: Path uses ~ but expandPath doesn't resolve it in the user's context; path is relative to a different working directory than where terraform runs; file permission is too restrictive; path was correct in CI but wrong locally; typo in the path string; file is outside the container/volume mount in containerized runs.
Related errors
- can not read leaf private key from %s
- can't read %s: %v
- can not get private_key or private_key_path from Terraform c
- can not get working directory for current os platform
- can not read leaf certificate from %s
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/be28f173b497cc8d.
Report an issue: GitHub.