helm/helm · error

unable to read key file: %q: %w

Error message

unable to read key file: %q: %w

What it means

The key-file counterpart of the cert read in WithCertKeyPairFiles: after the cert file reads successfully, os.ReadFile is called on keyFile and any failure is wrapped with the quoted path. The same both-empty guard applies, so setting only the key path makes the option fail here (or earlier on the empty cert path). The wrapped error carries the concrete OS cause.

Source

Thrown at internal/tlsutil/tls.go:56

		return nil
	}
}

func WithCertKeyPairFiles(certFile, keyFile string) TLSConfigOption {
	return func(options *TLSConfigOptions) error {
		if certFile == "" && keyFile == "" {
			return nil
		}

		certPEMBlock, err := os.ReadFile(certFile)
		if err != nil {
			return fmt.Errorf("unable to read cert file: %q: %w", certFile, err)
		}

		keyPEMBlock, err := os.ReadFile(keyFile)
		if err != nil {
			return fmt.Errorf("unable to read key file: %q: %w", keyFile, err)
		}

		options.certPEMBlock = certPEMBlock
		options.keyPEMBlock = keyPEMBlock

		return nil
	}
}

func WithCAFile(caFile string) TLSConfigOption {
	return func(options *TLSConfigOptions) error {
		if caFile == "" {
			return nil
		}

		caPEMBlock, err := os.ReadFile(caFile)
		if err != nil {
			return fmt.Errorf("can't read CA file: %q: %w", caFile, err)

View on GitHub (pinned to 2a29f1770b)

Solutions

  1. Verify the quoted path exists and is readable: ls -l <path>
  2. Supply both cert and key paths together — one without the other is rejected
  3. Prefer absolute paths for credentials
  4. Correct ownership/permissions (e.g., chown/chmod 600 for the key) when the cause is access-related

Example fix

# before
cfg, err := tlsutil.NewTLSConfig(tlsutil.WithCertKeyPairFiles(certPath, ""))

# after
cfg, err := tlsutil.NewTLSConfig(tlsutil.WithCertKeyPairFiles(certPath, keyPath))
Defensive patterns

Strategy: validation

Validate before calling

func validateTLSFilePair(certFile, keyFile string) error {
	if (certFile == "") != (keyFile == "") {
		return fmt.Errorf("cert and key files must be provided together")
	}
	for _, f := range []string{certFile, keyFile} {
		if f == "" {
			return nil
		}
		if _, err := os.Stat(f); err != nil {
			return fmt.Errorf("stat %s: %w", f, err)
		}
	}
	return nil
}

Try / catch

cfg, err := tlsutil.NewTLSConfig(tlsutil.WithCertKeyPairFiles(cert, key))
if err != nil {
	var pathErr *fs.PathError
	if errors.As(err, &pathErr) && errors.Is(pathErr, fs.ErrPermission) {
		// key file present but unreadable by this user: fix ownership/permissions
	}
	return err
}

Prevention

When it happens

Trigger: NewTLSConfig with WithCertKeyPairFiles where keyFile does not exist, is a directory, or is unreadable, or where keyFile is "" while certFile is set. Relative key paths resolved from an unexpected working directory also land here.

Common situations: Key file held in a secret not mounted at the expected path; key flagged 600 and owned by another user; only --key-file given on the command line; path with a typo or trailing whitespace from a shell variable.

Related errors


AI-assisted analysis of helm/helm@2a29f1770b (2026-08-15). Data as JSON: /api/errors/f72652f6993f9436. Report an issue: GitHub.