{"record":{"id":"839a0c8c661580cb","repo":"XTLS/Xray-core","slug":"exactly-one-item-kind-must-be-set","errorCode":null,"errorMessage":"exactly one item kind must be set","messagePattern":"exactly one item kind must be set","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"infra/conf/transport_finalmask.go","lineNumber":401,"sourceCode":"\tif err := validateCustomVarName(reuse); err != nil {\n\t\treturn err\n\t}\n\n\tkindCount := 0\n\tif len(packet) > 0 {\n\t\tkindCount++\n\t}\n\tif rand > 0 {\n\t\tkindCount++\n\t}\n\tif reuse != \"\" {\n\t\tkindCount++\n\t}\n\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}","sourceCodeStart":383,"sourceCodeEnd":419,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/infra/conf/transport_finalmask.go#L383-L419","documentation":"validateCustomItemSpec (infra/conf/transport_finalmask.go:401) counts how many 'kinds' a header-custom item sets among packet, rand>0, reuse, and transform. More than one kind returns \"exactly one item kind must be set\": an item must be a single, unambiguous producer of bytes or a variable reference.","triggerScenarios":"An item like {\"packet\":[1],\"rand\":5} (packet + rand), {\"reuse\":\"v\",\"transform\":{...}}, or {\"packet\":[1],\"reuse\":\"v\"}. Combinations with capture are fine as long as exactly one producer kind is present.","commonSituations":"Kitchen-sink templates filling every field; incremental editing where a transform is added without removing the old packet; copy-pasting an example item and toggling fields on.","solutions":["Keep exactly one of: packet, rand(>0), reuse, or transform per item","If you need both a packet and computed bytes, make them two sequential items","Re-read each item after editing and delete unused producer fields"],"exampleFix":"// before\n{ \"packet\": [1,2,3], \"rand\": 8 }\n// after\n{ \"packet\": [1,2,3] }\n// next item: { \"rand\": 8 }","handlingStrategy":"validation","validationCode":"function kinds(it:any){\n  let n = 0;\n  if (it.packet != null && it.packet !== '' && !(Array.isArray(it.packet)&&it.packet.length===0)) n++;\n  if ((it.rand ?? 0) > 0) n++;\n  if (it.reuse) n++;\n  if (it.transform) n++;\n  return n;\n}\nfor (const it of items) if (kinds(it) > 1) throw new Error('item sets multiple kinds');","typeGuard":"const hasSingleKind = (it:any) => kinds(it) === 1;","tryCatchPattern":null,"preventionTips":["Think of each item as exactly one producer","Never leave leftover producer fields when switching an item's role"],"tags":["xray","go","transport","header-custom","validation"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}