AdguardTeam/AdGuardHome · error

opening dnscrypt config: %w

Error message

opening dnscrypt config: %w

What it means

The DNSCrypt resolver configuration file could not be opened. os.Open on DNSCryptConfigFile failed; the wrapped error is typically 'no such file or directory' or 'permission denied'.

Source

Thrown at internal/home/dns.go:380

}

// newDNSCryptConfig converts values from the configuration file into the
// internal DNSCrypt settings for the DNS server.  extTLSConf must not be nil.
func newDNSCryptConfig(
	extTLSConf *aghtls.ExtendedTLSConfig,
	addrs []netip.Addr,
) (dnsCryptConf *dnsforward.DNSCryptConfig, err error) {
	if extTLSConf.PortDNSCrypt == 0 {
		return nil, nil
	}

	if extTLSConf.DNSCryptConfigFile == "" {
		return nil, fmt.Errorf("dnscrypt_config_file: %w", errors.ErrEmptyValue)
	}

	f, err := os.Open(extTLSConf.DNSCryptConfigFile)
	if err != nil {
		return nil, fmt.Errorf("opening dnscrypt config: %w", err)
	}
	defer func() { err = errors.WithDeferred(err, f.Close()) }()

	rc := &dnscrypt.ResolverConfig{}
	err = yaml.NewDecoder(f).Decode(rc)
	if err != nil {
		return nil, fmt.Errorf("decoding dnscrypt config: %w", err)
	}

	cert, err := rc.NewCert()
	if err != nil {
		return nil, fmt.Errorf("creating dnscrypt cert: %w", err)
	}

	return &dnsforward.DNSCryptConfig{
		ResolverCert:   cert,
		UDPListenAddrs: ipsToUDPAddrs(addrs, extTLSConf.PortDNSCrypt),
		TCPListenAddrs: ipsToTCPAddrs(addrs, extTLSConf.PortDNSCrypt),

View on GitHub (pinned to b41aefbe51)

Solutions

  1. Confirm the file exists at the exact configured path: ls -l <path>
  2. Fix ownership/permissions so the service user can read it (chmod 644 or chown)
  3. Correct the dnscrypt_config_file value in config.yaml
  4. In containers, verify the volume mount actually exposes the file

Example fix

# before
tls:
  dnscrypt_config_file: /etc/adguard/dnscrypt.yaml   # file not present
# after
sudo cp ~/dnscrypt.yaml /etc/adguard/dnscrypt.yaml
sudo chmod 644 /etc/adguard/dnscrypt.yaml
tls:
  dnscrypt_config_file: /etc/adguard/dnscrypt.yaml
Defensive patterns

Strategy: validation

Validate before calling

if _, err := os.Stat(tlsConf.DNSCryptConfigFile); err != nil {
    return fmt.Errorf("dnscrypt config file %q: %w", tlsConf.DNSCryptConfigFile, err)
}

Type guard

func fileReadable(p string) bool {
    f, err := os.Open(p)
    if err != nil { return false }
    f.Close()
    return true
}

Try / catch

if err != nil {
    if errors.Is(err, fs.ErrNotExist) {
        log.Error("dnscrypt config missing; recreating from template")
    } else if errors.Is(err, fs.ErrPermission) {
        log.Error("fix permissions on dnscrypt config")
    }
}

Prevention

When it happens

Trigger: dnscrypt_config_file pointing to a non-existent path, or a path the AdGuard Home process user cannot read (wrong permissions, SELinux denial, container volume not mounted).

Common situations: Typo'd path in config.yaml, file deleted or moved during upgrade, running in Docker without mounting the file, restrictive ownership (e.g. root-owned 600 file read by an unprivileged service user).

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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