syncthing/syncthing · warning

UPnP Error: %s (%d)

Error message

UPnP Error: %s (%d)

What it means

The IGD gateway returned a SOAP fault for AddPortMapping that is not error 725 (which syncthing auto-repairs by retrying with a permanent lease). The gateway's own description and error code are embedded, e.g. 718 ConflictInMappingEntry, 727 ExternalPortOnlySupportsWildcard, 606 ActionNotAuthorized.

Source

Thrown at lib/upnp/igd_service.go:211

		<NewEnabled>1</NewEnabled>
		<NewPortMappingDescription>%s</NewPortMappingDescription>
		<NewLeaseDuration>%d</NewLeaseDuration>
		</u:AddPortMapping>`
	body := fmt.Sprintf(template, s.URN, externalPort, protocol, internalPort, s.LocalIPv4, description, duration/time.Second)

	response, err := soapRequestWithIP(ctx, s.URL, s.URN, "AddPortMapping", body, &net.TCPAddr{IP: s.LocalIPv4})
	if err != nil && duration > 0 {
		// Try to repair error code 725 - OnlyPermanentLeasesSupported
		var envelope soapErrorResponse
		if unmarshalErr := xml.Unmarshal(response, &envelope); unmarshalErr != nil {
			return externalPort, unmarshalErr
		}

		if envelope.ErrorCode == 725 {
			return s.AddPortMapping(ctx, protocol, internalPort, externalPort, description, 0)
		}

		err = fmt.Errorf("UPnP Error: %s (%d)", envelope.ErrorDescription, envelope.ErrorCode)
		l.Debugf("Couldn't add port mapping for %s (external port %d -> internal port %d/%s): %s", s.LocalIPv4, externalPort, internalPort, protocol, err)
	}

	return externalPort, err
}

// DeletePortMapping deletes a port mapping from the specified IGD service.
func (s *IGDService) DeletePortMapping(ctx context.Context, protocol nat.Protocol, externalPort int) error {
	const template = `<u:DeletePortMapping xmlns:u="%s">
	<NewRemoteHost></NewRemoteHost>
	<NewExternalPort>%d</NewExternalPort>
	<NewProtocol>%s</NewProtocol>
	</u:DeletePortMapping>`

	body := fmt.Sprintf(template, s.URN, externalPort, protocol)

	_, err := soapRequest(ctx, s.URL, s.URN, "DeletePortMapping", body)
	return err

View on GitHub (pinned to 058bcd7334)

Solutions

  1. Match the error code: 718 → change to a different external port; 606 → enable UPnP or authorize the client in the router; 728 → reboot router to clear the full mapping table
  2. Delete the conflicting static port-forward in the router admin UI, or pick a unique external port for this device
  3. Set 'externalPort' in syncthing's config for the device instead of automatic discovery
  4. Fall back to manual port forwarding and disable NAT discovery in syncthing if the gateway's UPnP is broken
Defensive patterns

Strategy: fallback

Try / catch

extPort, err := svc.AddPortMapping(ctx, proto, intPort, extPort, desc, lease)
if err != nil {
    if strings.Contains(err.Error(), "718") { // ConflictInMappingEntry
        // retry with a different external port
    }
    // else disable auto-mapping for this device and configure the forward manually
}

Prevention

When it happens

Trigger: IGDService.AddPortMapping with duration > 0 where the gateway rejects the mapping: another host already holds the external port (718), the router requires authorization (606), or the description/port parameters exceed what the device supports. Note the 725 path recursively retries with duration 0.

Common situations: Two devices behind the same router requesting the same external port; router UPnP table exhausted; ISP gateways with UPnP disabled/filtered (failure/606); conflicting static port-forward configured in the router admin UI.

Related errors


AI-assisted analysis of syncthing/syncthing@058bcd7334 (2026-08-15). Data as JSON: /api/errors/d9a6f9c82d02a646. Report an issue: GitHub.