XTLS/Xray-core · error

AddRule: config type error

Error message

AddRule: config type error

What it means

AddRule expects the TypedMessage payload to decode to the router's *Config type (a full routing config). Any other message type is rejected because rule addition is implemented as a partial config reload.

Source

Thrown at app/router/router.go:120

	if err != nil {
		return nil, err
	}
	if rule.Webhook != nil {
		rule.Webhook.Fire(originalCtx, tag)
	}
	return &Route{Context: ctx, outboundTag: tag, ruleTag: rule.RuleTag}, nil
}

// AddRule implements routing.Router.
func (r *Router) AddRule(config *serial.TypedMessage, shouldAppend bool) error {
	inst, err := config.GetInstance()
	if err != nil {
		return err
	}
	if c, ok := inst.(*Config); ok {
		return r.ReloadRules(c, shouldAppend)
	}
	return errors.New("AddRule: config type error")
}

func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
	r.mu.Lock()
	defer r.mu.Unlock()

	if !shouldAppend {
		for _, rule := range r.rules {
			if rule.Webhook != nil {
				rule.Webhook.Close()
			}
		}
		r.balancers = make(map[string]*Balancer, len(config.BalancingRule))
		r.rules = make([]*Rule, 0, len(config.Rule))
	}
	for _, rule := range config.BalancingRule {
		_, found := r.balancers[rule.Tag]
		if found {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Wrap the rule inside a routing Config message: TypedMessage of app.router.Config containing the new rule
  2. Use a helper (serial.ToTypedMessage(&router.Config{Rule: [...]})) to build the payload
  3. Verify the @type string matches the registered Config message

Example fix

// before
msg := serial.ToTypedMessage(&router.Rule{}) // wrong type
router.AddRule(msg, true) // config type error

// after
msg := serial.ToTypedMessage(&router.Config{Rule: []*router.RoutingRule{rule}})
router.AddRule(msg, true)
Defensive patterns

Strategy: type-guard

Validate before calling

inst, err := msg.GetInstance()
if err != nil { return err }
if _, ok := inst.(*router.Config); !ok {
    return fmt.Errorf("AddRule expects app.router.Config, got %T", inst)
}

Type guard

func isRouterConfig(msg *serial.TypedMessage) bool {
    i, err := msg.GetInstance()
    return err == nil && isConfigType(i)
}
func isConfigType(i interface{}) bool { _, ok := i.(*router.Config); return ok }

Prevention

When it happens

Trigger: Calling AddRule with a serial.TypedMessage whose @type resolves to something other than app.router.Config (e.g. a raw rule message or a config from a different module).

Common situations: Clients assuming AddRule takes a single rule object; cross-version protobuf type URLs; hand-built TypedMessage with wrong type URL.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/8b016d4f7010e850. Report an issue: GitHub.