AdguardTeam/AdGuardHome · error
dhcpv4 or dhcpv6 configuration must be complete
Error message
dhcpv4 or dhcpv6 configuration must be complete
What it means
Returned by createServers when the config has Enabled set to true (aghalg.NBTrue) but neither the DHCPv4 nor the DHCPv6 sub-configuration turned out to be complete/enabled. It prevents starting a DHCP 'server' that would actually serve nothing on either protocol.
Source
Thrown at internal/dhcpd/http_unix.go:310
return srv6, enabled, err
}
// createServers returns DHCPv4 and DHCPv6 servers created from the provided
// configuration conf.
func (s *server) createServers(conf *dhcpServerConfigJSON) (srv4, srv6 DHCPServer, err error) {
srv4, v4Enabled, err := s.handleDHCPSetConfigV4(conf)
if err != nil {
return nil, nil, fmt.Errorf("bad dhcpv4 configuration: %w", err)
}
srv6, v6Enabled, err := s.handleDHCPSetConfigV6(conf)
if err != nil {
return nil, nil, fmt.Errorf("bad dhcpv6 configuration: %w", err)
}
if conf.Enabled == aghalg.NBTrue && !v4Enabled && !v6Enabled {
return nil, nil, fmt.Errorf("dhcpv4 or dhcpv6 configuration must be complete")
}
return srv4, srv6, nil
}
// handleDHCPSetConfig is the handler for the POST /control/dhcp/set_config
// HTTP API.
func (s *server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
l := s.conf.Logger
conf := &dhcpServerConfigJSON{}
conf.Enabled = aghalg.BoolToNullBool(s.conf.Enabled)
conf.InterfaceName = s.conf.InterfaceName
err := json.NewDecoder(r.Body).Decode(conf)
if err != nil {
aghhttp.ErrorAndLog(View on GitHub (pinned to b41aefbe51)
Solutions
- Fully specify at least one protocol block (v4 or v6): interface, range start/end, gateway/subnet as required
- Check the per-protocol enabled flags inside v4/v6 blocks are not false while expecting the server to serve them
- Re-read the config with GET /control/dhcp/config to see which block is incomplete, then fix and re-POST
Example fix
// before
{"enabled":true,"v4":{},"v6":{}}
// -> 400 dhcpv4 or dhcpv6 configuration must be complete
// after
{"enabled":true,"v4":{"enabled":true,"interface_name":"eth0","range_start":"192.168.1.50","range_end":"192.168.1.100","subnet_mask":"255.255.255.0","gateway_ip":"192.168.1.1"},"v6":{}} Defensive patterns
Strategy: validation
Validate before calling
func configComplete(c Config) bool {
return c.V4.enabledAndFilled() || c.V6.enabledAndFilled()
// i.e. at least one block has interface + valid range + required IPs
} Prevention
- Always fill at least one protocol block completely before setting enabled=true
- Use the UI wizard flow which enforces per-protocol completeness
- GET the current config and diff before POSTing to spot empty blocks
When it happens
Trigger: POSTing {"enabled": true} with both v4 and v6 blocks disabled, empty, or lacking the required fields (e.g. no range set), so v4Enabled and v6Enabled both come back false.
Common situations: Flipping the master Enabled toggle in the UI before filling in either protocol's settings; disabling v4 while forgetting to complete the v6 block; partial configs left over from migrations.
Related errors
- bad dhcpv4 configuration: %w
- starting dhcp server: %w
- bad dhcpv6 configuration: %w
- start is greater than or equal to end
- range is too large
AI-assisted analysis of AdguardTeam/AdGuardHome@b41aefbe51 (2026-08-27).
Data as JSON: /api/errors/3f8652f9cf341c41.
Report an issue: GitHub.