TechnitiumSoftware/DnsServer · error · InvalidDataException

External to internal translation entries must have same pref

Error message

External to internal translation entries must have same prefix length: {strExternal} - {strInternal}

What it means

SplitHorizonApp's external-to-internal translation additionally requires both endpoints to share the same prefix length (e.g. /24 to /24, /32 to /32). This InvalidDataException fires when the external and internal NetworkAddress values have matching families but different PrefixLength values, since asymmetric prefixes would translate the wrong number of addresses.

Source

Thrown at Apps/SplitHorizonApp/AddressTranslation.cs:389

                JsonElement jsonExternalToInternalTranslation = jsonGroup.GetProperty("externalToInternalTranslation");

                Dictionary<IPAddress, IPAddress> externalToInternalIpTranslation = new Dictionary<IPAddress, IPAddress>();
                Dictionary<IPAddress, IPAddress> internalToExternalIpTranslation = new Dictionary<IPAddress, IPAddress>();
                List<KeyValuePair<NetworkAddress, NetworkAddress>> externalToInternalNetworkTranslation = new List<KeyValuePair<NetworkAddress, NetworkAddress>>();

                foreach (JsonProperty jsonProperty in jsonExternalToInternalTranslation.EnumerateObject())
                {
                    string strExternal = jsonProperty.Name;
                    string strInternal = jsonProperty.Value.GetString();

                    NetworkAddress external = NetworkAddress.Parse(strExternal);
                    NetworkAddress @internal = NetworkAddress.Parse(strInternal);

                    if (external.AddressFamily != @internal.AddressFamily)
                        throw new InvalidDataException("External to internal translation entries must have same address family: " + strExternal + " - " + strInternal);

                    if (external.PrefixLength != @internal.PrefixLength)
                        throw new InvalidDataException("External to internal translation entries must have same prefix length: " + strExternal + " - " + strInternal);

                    if (
                        ((external.AddressFamily == AddressFamily.InterNetwork) && (external.PrefixLength == 32)) ||
                        ((external.AddressFamily == AddressFamily.InterNetworkV6) && (external.PrefixLength == 128))
                       )
                    {
                        externalToInternalIpTranslation.TryAdd(external.Address, @internal.Address);

                        if (_translateReverseLookups)
                            internalToExternalIpTranslation.TryAdd(@internal.Address, external.Address);
                    }
                    else
                    {
                        externalToInternalNetworkTranslation.Add(new KeyValuePair<NetworkAddress, NetworkAddress>(external, @internal));
                    }
                }

                _externalToInternalTranslation = externalToInternalIpTranslation;

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Match the prefix length on both sides of each translation entry.
  2. For single-host translation use /32 (IPv4) or /128 (IPv6) on both external and internal.
  3. If pool sizes genuinely differ, split into multiple equal-prefix entries instead.

Example fix

// before
"externalToInternalTranslation": {
  "203.0.113.0/29": "10.0.0.1/32"
}

// after
"externalToInternalTranslation": {
  "203.0.113.0/29": "10.0.0.0/29"
}
Defensive patterns

Strategy: validation

Validate before calling

static void AssertSamePrefixLength(string ext, string intl)
{
    var ep = int.Parse(ext.Split('/')[1]);
    var ip = int.Parse(intl.Split('/')[1]);
    if (ep != ip)
        throw new InvalidDataException($"Prefix length mismatch: {ext} - {intl}");
}

foreach (var (ext, intl) in translationEntries)
    AssertSamePrefixLength(ext, intl);

Try / catch

try { /* load translation config */ }
catch (InvalidDataException ex) when (ex.Message.Contains("same prefix length"))
{ /* report the entry and abort config apply */ }

Prevention

When it happens

Trigger: A translation entry where the external CIDR and internal CIDR differ in mask size, e.g. external '203.0.113.0/24' (256 addresses) mapped to internal '10.0.0.1/32' (single host).

Common situations: Configuring a /29 external block to a /24 internal subnet (or vice versa) thinking the translation auto-scales; forgetting that 1:1 NAT needs equal-size pools.

Related errors


AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13). Data as JSON: /api/errors/3e5889131742a7a0. Report an issue: GitHub.