nats-io/nats-server · error

OnReload, sort or explicitly skip type: %s

Error message

OnReload, sort or explicitly skip type: %s

What it means

applyOptions/diff machinery switches over every option type changed during reload; the default branch rejects types that were not sorted (mapped to a diff option) or explicitly skipped. This is a developer-facing guard so newly added config fields must be handled in the reload diff switch.

Source

Thrown at server/reload.go:1574

	case []*url.URL:
		slices.SortFunc(value, func(i, j *url.URL) int { return cmp.Compare(i.String(), j.String()) })
	case []string:
		slices.Sort(value)
	case []*jwt.OperatorClaims:
		slices.SortFunc(value, func(i, j *jwt.OperatorClaims) int { return cmp.Compare(i.Issuer, j.Issuer) })
	case GatewayOpts:
		slices.SortFunc(value.Gateways, func(i, j *RemoteGatewayOpts) int { return cmp.Compare(i.Name, j.Name) })
	case WebsocketOpts:
		slices.Sort(value.AllowedOrigins)
	case string, bool, uint8, uint16, uint64, int, int32, int64, time.Duration, float64, nil, LeafNodeOpts, ClusterOpts, *tls.Config, PinnedCertSet,
		*URLAccResolver, *MemAccResolver, *DirAccResolver, *CacheDirAccResolver, Authentication, MQTTOpts, jwt.TagList,
		*OCSPConfig, map[string]string, map[string]bool, JSLimitOpts, StoreCipher, *OCSPResponseCacheConfig, *ProxiesConfig, WriteTimeoutPolicy:
		// explicitly skipped types
	case *AuthCallout:
	case JSTpmOpts:
	default:
		// this will fail during unit tests
		return fmt.Errorf("OnReload, sort or explicitly skip type: %s",
			reflect.TypeOf(value))
	}
	return nil
}

// diffOptions returns a slice containing options which have been changed. If
// an option that doesn't support hot-swapping is changed, this returns an
// error.
func (s *Server) diffOptions(newOpts *Options) ([]option, error) {
	var (
		oldOpts   = s.getOpts()
		oldConfig = reflect.ValueOf(oldOpts).Elem()
		newConfig = reflect.ValueOf(newOpts).Elem()
		diffOpts  = []option{}
		skipTKeys = len(oldOpts.TrustedOperators) > 0 && len(oldOpts.TrustedKeys) > 0

		// Need to keep track of whether JS is being disabled
		// to prevent changing limits at runtime.

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Add a case for the missing type in the reload diff switch in server/reload.go, mapping it to an appropriate option.
  2. Add the type to the explicitly-skipped case list if the field does not support hot reload.
  3. Restart the server instead of reloading if you cannot modify the code handling the new option type.

Example fix

// before
default:
    return fmt.Errorf("OnReload, sort or explicitly skip type: %s", reflect.TypeOf(value))
// after
case *MyNewOptionType:
    diffOpts = append(diffOpts, &myNewOption{newValue: value.(*MyNewOptionType)})
Defensive patterns

Strategy: fallback

Try / catch

if err := srv.Reload(); err != nil {
    if strings.Contains(err.Error(), "OnReload, sort or explicitly skip type") {
        log.Fatalf("new option type not handled for reload: %v", err) // fall back to restart
    }
}

Prevention

When it happens

Trigger: A new options field's type (e.g. a custom struct) is changed at runtime and passed to the reload diff switch without a case or an explicit skip entry, causing this error during Reload().

Common situations: Contributors adding a new config option to server Options without updating server/reload.go's type switch; custom forks with extended Options fields being hot-reloaded.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/2d96ab065c121b8b. Report an issue: GitHub.