{"record":{"id":"78fb9f246427b050","repo":"XTLS/Xray-core","slug":"not-a-service","errorCode":null,"errorMessage":"not a Service.","messagePattern":"not a Service\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/commander/commander.go","lineNumber":50,"sourceCode":"\t\tlisten: config.Listen,\n\t}\n\n\tcommon.Must(core.RequireFeatures(ctx, func(om outbound.Manager) {\n\t\tc.ohm = om\n\t}))\n\n\tfor _, rawConfig := range config.Service {\n\t\tconfig, err := rawConfig.GetInstance()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\trawService, err := common.CreateObject(ctx, config)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tservice, ok := rawService.(Service)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"not a Service.\")\n\t\t}\n\t\tc.services = append(c.services, service)\n\t}\n\n\treturn c, nil\n}\n\n// Type implements common.HasType.\nfunc (c *Commander) Type() interface{} {\n\treturn (*Commander)(nil)\n}\n\n// Start implements common.Runnable.\nfunc (c *Commander) Start() error {\n\tc.Lock()\n\tc.server = grpc.NewServer()\n\tfor _, service := range c.services {\n\t\tservice.Register(c.server)","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/commander/commander.go#L32-L68","documentation":"Thrown by validatePaddingSchedule when a single paddingTurn sets both a list of explicit padding variants (turn.variants) and a send-length range (turn.sendMinLength/turn.sendMaxLength). The padding engine supports two mutually exclusive ways to size the bytes a side sends: precomputed variants or a randomized send range. Combining them is ambiguous, so the schedule is rejected before any traffic flows.","triggerScenarios":"A paddingTurn literal (or decoded JSON config) that has a non-empty variants array while sendMinLength or sendMaxLength is non-zero. runPaddingSchedule calls validatePaddingSchedule up front, so this fires immediately at handshake/startup, before any network I/O.","commonSituations":"Copying a variants-based schedule and pasting a send-range block into the same turn; migrating config from range-style padding to variant-style padding without removing the old sendMinLength/sendMaxLength fields; defaults in a shared template that pre-populate sendMinLength.","solutions":["Remove sendMinLength/sendMaxLength from the turn so only variants size the traffic","Or remove the variants array and express the turn purely as minLength/maxLength plus sendMinLength/sendMaxLength","Re-run the connection; validation happens at schedule start, so no restart state is carried over"],"exampleFix":"// before\npaddingTurn{\n  direction: paddingClientToServer,\n  variants: []paddingVariant{...},\n  sendMinLength: 100,\n  sendMaxLength: 400,\n}\n// after\npaddingTurn{\n  direction: paddingClientToServer,\n  variants: []paddingVariant{...},\n}","handlingStrategy":"validation","validationCode":"func turnIsConsistent(t paddingTurn) bool {\n    hasSendRange := t.sendMinLength != 0 || t.sendMaxLength != 0\n    return !(len(t.variants) > 0 && hasSendRange)\n}\n// before dialing:\nfor i, t := range schedule {\n    if !turnIsConsistent(t) {\n        return fmt.Errorf(\"turn %d mixes variants and send range\", i)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build schedules through one constructor that enforces either variants or a send range, never both","Assert turnIsConsistent in schedule unit tests","Encode the invariant in JSON schema/validation for user-supplied configs"],"tags":["padding","config","validation","xmc"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}