XTLS/Xray-core · error

transform op is required

Error message

transform op is required

What it means

buildCustomTransform (infra/conf/transport_finalmask.go:415) requires every transform object to name its operation: "transform":{"op":""} (or op omitted) fails with "transform op is required". The op string selects the expression/function applied to transform args when building the custom.Expr.

Source

Thrown at infra/conf/transport_finalmask.go:415

	if transform != nil {
		kindCount++
	}
	if kindCount > 1 {
		return errors.New("exactly one item kind must be set")
	}
	if kindCount == 0 && capture != "" {
		return errors.New("exactly one item kind must be set")
	}

	return nil
}

func buildCustomTransform(transform *CustomTransform) (*custom.Expr, error) {
	if transform == nil {
		return nil, nil
	}
	if transform.Op == "" {
		return nil, errors.New("transform op is required")
	}
	if len(transform.Args) == 0 {
		return nil, errors.New("transform args are required")
	}

	args := make([]*custom.ExprArg, 0, len(transform.Args))
	for _, arg := range transform.Args {
		parsedArg, err := buildCustomTransformArg(arg)
		if err != nil {
			return nil, err
		}
		args = append(args, parsedArg)
	}

	return &custom.Expr{
		Op:   transform.Op,
		Args: args,
	}, nil

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Set "op" to a supported operation name in every transform object, including nested ones
  2. Check the project's transform op vocabulary (see usages of custom.Expr / docs) before inventing names
  3. If no operation is needed, remove the transform entirely

Example fix

// before
"transform": { "args": [ { "reuse": "v" } ] }
// after
"transform": { "op": "concat", "args": [ { "reuse": "v" } ] }
Defensive patterns

Strategy: validation

Validate before calling

function checkTransform(t:any){
  if (!t) return;
  if (!t.op) throw new Error('transform.op required');
  if (!Array.isArray(t.args) || t.args.length === 0) throw new Error('transform.args required');
  for (const a of t.args) if (a.transform) checkTransform(a.transform);
}
for (const it of items) checkTransform(it.transform);

Type guard

const isTransform = (t:any) => t == null || (typeof t.op === 'string' && t.op !== '' && Array.isArray(t.args) && t.args.length > 0);

Prevention

When it happens

Trigger: Any item with "transform":{"args":[...]} but no "op", or op:"" . Also triggered inside nested transform args (buildCustomTransformArg recurses into arg.Transform) with the same defect.

Common situations: Writing a transform to wrap a variable and forgetting the operation name; trimming an example transform down to args only; nested transforms where the inner object lacks op.

Related errors


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