XTLS/Xray-core · error · errors.Error

failed to build final rule

Error message

failed to build final rule

What it means

Thrown by freedom outbound's Init when one of the configured settings.finalRules entries cannot be compiled by buildFinalRule. The freedom handler iterates config.FinalRules at startup and aborts on the first rule that fails to parse (bad target address, unknown protocol, or empty rule fields). The underlying cause is attached via .Base(err).

Source

Thrown at proxy/freedom/freedom.go:214

	return nil
}

func (h *Handler) applyFinalRules(network net.Network, address net.Address, port net.Port, defaultRule *FinalRule) RuleAction {
	if rule := h.matchFinalRule(network, address, port, defaultRule); rule != nil {
		return rule.action
	}
	return RuleAction_Allow
}

// Init initializes the Handler with necessary parameters.
func (h *Handler) Init(config *Config, pm policy.Manager) error {
	h.config = config
	h.policyManager = pm
	h.finalRules = make([]*FinalRule, 0, len(config.FinalRules))
	for _, rc := range config.FinalRules {
		rule, err := buildFinalRule(rc)
		if err != nil {
			return errors.New("failed to build final rule").Base(err)
		}
		h.finalRules = append(h.finalRules, rule)
	}
	return nil
}

func (h *Handler) policy() policy.Session {
	p := h.policyManager.ForLevel(h.config.UserLevel)
	return p
}

func (h *Handler) blockDelay(rule *FinalRule) time.Duration {
	min := uint64(30)
	max := uint64(90)
	if rule.blockDelay != nil {
		min = rule.blockDelay.Min
		max = rule.blockDelay.Max
	}

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Inspect the wrapped error (err) returned alongside the message — buildFinalRule names which rule field failed
  2. Validate every entry of settings.finalRules against the current Xray JSON schema (target, port, protocol/action fields)
  3. Run `xray run -test -c config.json` (or xlat) to lint the config before deploying
  4. Remove or fix the single offending rule; other rules are fine because iteration stops at the first failure

Example fix

// before
"settings": { "finalRules": [ { "target": "example.com", "port": "http" } ] }
// after
"settings": { "finalRules": [ { "target": "example.com", "port": "80,443", "action": "reject" } ] }
Defensive patterns

Strategy: validation

Validate before calling

// before building the handler, dry-run the config
if err := freedomHandlerCreator; err != nil { ... }
// simplest: `xray run -test -c config.json` in CI catches buildFinalRule failures

Try / catch

err := handler.Init(cfg, pm)
if err != nil {
  if strings.Contains(err.Error(), "failed to build final rule") {
    // unwrap err via errors.Unwrap to find the offending rule field
  }
}

Prevention

When it happens

Trigger: Configuring an outbound with "protocol":"freedom" and a settings.finalRules array where an entry has an invalid/malformed target (e.g. bad CIDR, unparseable port range) or missing required fields; Init returns this before any traffic is processed.

Common situations: Hand-edited JSON config with typos in finalRules; migrating configs between Xray versions where the finalRule schema changed; copying example configs that use fields the current build does not support.

Related errors


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