AdguardTeam/AdGuardHome · critical

reading key file: %w

Error message

reading key file: %w

What it means

The manager could not read the TLS private key file from disk while loading TLS configuration. It wraps an os.ReadFile failure (ENOENT, EACCES, etc.) on the configured PrivateKeyPath.

Source

Thrown at internal/aghtls/defaultmanager.go:671

	}

	return nil
}

// loadPrivateKeyData loads PEM-encoded private key data to the TLS
// configuration. tlsConf must be not nil. tlsConf.PrivateKeyData struct field
// will be modified in case tlsConfig.PrivateKeyPath is not an empty string.
// extTLSConf must not be nil.
func loadPrivateKeyData(extTLSConf *ExtendedTLSConfig) (err error) {
	extTLSConf.PrivateKeyData = []byte(extTLSConf.PrivateKey)
	if extTLSConf.PrivateKeyPath != "" {
		if extTLSConf.PrivateKey != "" {
			return errors.Error("private key data and file can't be set together")
		}

		extTLSConf.PrivateKeyData, err = os.ReadFile(extTLSConf.PrivateKeyPath)
		if err != nil {
			return fmt.Errorf("reading key file: %w", err)
		}
	}

	return nil
}

// validateCertChain verifies certs using the first as the main one and others
// as intermediate.  srvName stands for the expected DNS name.  certs must not
// be empty.  logger must not be nil.
func validateCertChain(
	ctx context.Context,
	logger *slog.Logger,
	rootCAs *x509.CertPool,
	certs []*x509.Certificate,
	srvName string,
) (err error) {
	main, others := certs[0], certs[1:]

View on GitHub (pinned to b41aefbe51)

Solutions

  1. Check existence and permissions of the key file; ensure the service user can read it (often needs group-read with restricted group)
  2. Align provisioning so the key exists before the service starts
  3. Remove the inline PrivateKey if using PrivateKeyPath (they are mutually exclusive)
  4. Confirm PrivateKeyPath points to the key, not the certificate

Example fix

// before
private_key_path: /etc/ssl/certs/example.com.pem # actually the cert
// after
private_key_path: /etc/ssl/private/example.com.key
Defensive patterns

Strategy: validation

Validate before calling

if info, err := os.Stat(conf.PrivateKeyPath); err != nil || info.IsDir() {
    return fmt.Errorf("key path invalid: %s", conf.PrivateKeyPath)
}

Try / catch

if err := mgr.LoadTLSConfig(ctx, conf); err != nil {
    if errors.Is(err, fs.ErrPermission) { /* fix ownership; do not run as root long-term */ }
}

Prevention

When it happens

Trigger: LoadTLSConfig with a PrivateKeyPath that is missing, unreadable due to permissions (common: root-owned 600 key read by non-root process), or set while PrivateKey data is also provided (preceding guard error).

Common situations: Key file permission mismatch when running the service as an unprivileged user; key provisioned later than startup; mismatched path (key vs cert swapped); both inline PrivateKey and PrivateKeyPath configured.

Related errors


AI-assisted analysis of AdguardTeam/AdGuardHome@b41aefbe51 (2026-08-27). Data as JSON: /api/errors/f891d9b93ce5cbdc. Report an issue: GitHub.