{"record":{"id":"cb0fc493e96c0aa8","repo":"XTLS/Xray-core","slug":"transform-op-is-required","errorCode":null,"errorMessage":"transform op is required","messagePattern":"transform op is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"infra/conf/transport_finalmask.go","lineNumber":415,"sourceCode":"\tif transform != nil {\n\t\tkindCount++\n\t}\n\tif kindCount > 1 {\n\t\treturn errors.New(\"exactly one item kind must be set\")\n\t}\n\tif kindCount == 0 && capture != \"\" {\n\t\treturn errors.New(\"exactly one item kind must be set\")\n\t}\n\n\treturn nil\n}\n\nfunc buildCustomTransform(transform *CustomTransform) (*custom.Expr, error) {\n\tif transform == nil {\n\t\treturn nil, nil\n\t}\n\tif transform.Op == \"\" {\n\t\treturn nil, errors.New(\"transform op is required\")\n\t}\n\tif len(transform.Args) == 0 {\n\t\treturn nil, errors.New(\"transform args are required\")\n\t}\n\n\targs := make([]*custom.ExprArg, 0, len(transform.Args))\n\tfor _, arg := range transform.Args {\n\t\tparsedArg, err := buildCustomTransformArg(arg)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\targs = append(args, parsedArg)\n\t}\n\n\treturn &custom.Expr{\n\t\tOp:   transform.Op,\n\t\tArgs: args,\n\t}, nil","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/infra/conf/transport_finalmask.go#L397-L433","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set \"op\" to a supported operation name in every transform object, including nested ones","Check the project's transform op vocabulary (see usages of custom.Expr / docs) before inventing names","If no operation is needed, remove the transform entirely"],"exampleFix":"// before\n\"transform\": { \"args\": [ { \"reuse\": \"v\" } ] }\n// after\n\"transform\": { \"op\": \"concat\", \"args\": [ { \"reuse\": \"v\" } ] }","handlingStrategy":"validation","validationCode":"function checkTransform(t:any){\n  if (!t) return;\n  if (!t.op) throw new Error('transform.op required');\n  if (!Array.isArray(t.args) || t.args.length === 0) throw new Error('transform.args required');\n  for (const a of t.args) if (a.transform) checkTransform(a.transform);\n}\nfor (const it of items) checkTransform(it.transform);","typeGuard":"const isTransform = (t:any) => t == null || (typeof t.op === 'string' && t.op !== '' && Array.isArray(t.args) && t.args.length > 0);","tryCatchPattern":null,"preventionTips":["Recursively validate transforms including nested arg transforms","Template every transform as {op, args} from the start"],"tags":["xray","go","transport","header-custom","transform","validation"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}