{"record":{"id":"f19f7fa6e4422742","repo":"fatedier/frp","slug":"invalid-pool-count-d-must-be-non-negative","errorCode":null,"errorMessage":"invalid pool count %d, must be non-negative","messagePattern":"invalid pool count (.+?), must be non-negative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/control.go","lineNumber":438,"sourceCode":"\tlifecycleMu    sync.Mutex\n\tstate          controlState\n\tactivated      bool\n\thandoffBarrier <-chan struct{}\n\n\tinterruptOnce sync.Once\n\tinterruptErr  error\n\n\tmu sync.RWMutex\n\n\txl            *xlog.Logger\n\tctx           context.Context\n\tdoneCh        chan struct{}\n\tserverMetrics metrics.ServerMetrics\n}\n\nfunc NewControl(ctx context.Context, sessionCtx *SessionContext) (*Control, error) {\n\tif sessionCtx.LoginMsg.PoolCount < 0 {\n\t\treturn nil, fmt.Errorf(\"invalid pool count %d, must be non-negative\", sessionCtx.LoginMsg.PoolCount)\n\t}\n\tif sessionCtx.ServerCfg.Transport.MaxPoolCount < 0 {\n\t\treturn nil, fmt.Errorf(\n\t\t\t\"invalid max pool count %d, must be non-negative\",\n\t\t\tsessionCtx.ServerCfg.Transport.MaxPoolCount,\n\t\t)\n\t}\n\teffectivePoolCount := min(int64(sessionCtx.LoginMsg.PoolCount), sessionCtx.ServerCfg.Transport.MaxPoolCount)\n\tmaxPoolCountForChannel := int64(math.MaxInt) - int64(workConnPoolCapacityOffset)\n\tif effectivePoolCount > maxPoolCountForChannel {\n\t\treturn nil, fmt.Errorf(\n\t\t\t\"invalid effective pool count %d, cannot safely add %d for work connection pool capacity\",\n\t\t\teffectivePoolCount, workConnPoolCapacityOffset,\n\t\t)\n\t}\n\tpoolCount := int(effectivePoolCount)\n\tctl := &Control{\n\t\tsessionCtx:    sessionCtx,","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/server/control.go#L420-L456","documentation":"NewControl validates the login message from frpc: LoginMsg.PoolCount must be non-negative. A negative pool count is a malformed request, so control creation is refused before any resources are allocated.","triggerScenarios":"A client sends a Login message with PoolCount < 0 — only possible from a hand-rolled or corrupted client, since stock frpc always sends >= 0. Also reachable from unit tests or custom clients built against the msg package.","commonSituations":"Custom clients/replays with wrong field types; integer underflow in generated login messages; fuzzing the login endpoint; protocol deserialization bugs in third-party clients.","solutions":["If you write a custom client, always send a zero-or-positive PoolCount (0 means create work conns on demand).","Inspect the raw login frame (enable trace logging on frps) to find who sends a negative value.","Upgrade mismatched frpc builds to a released version."],"exampleFix":"// custom client — before\nloginMsg.PoolCount = -1\n\n// after\nloginMsg.PoolCount = 0","handlingStrategy":"validation","validationCode":"// custom client before login\nif loginMsg.PoolCount < 0 { loginMsg.PoolCount = 0 }","typeGuard":null,"tryCatchPattern":"if _, err := server.NewControl(ctx, sessionCtx); err != nil {\n    return err // malformed login: reject session, do not retry unchanged\n}","preventionTips":["Always default-construct login messages; Go zero values satisfy the invariant.","Fuzz-test your client's serialisation of numeric fields."],"tags":["go","frp","validation","login","protocol"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}