{"record":{"id":"281d87648f3d8dfb","repo":"rqlite/rqlite","slug":"connection-is-nil-rejecting","errorCode":null,"errorMessage":"connection is nil. rejecting","messagePattern":"connection is nil\\. rejecting","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"tcp/pool/channel.go","lineNumber":122,"sourceCode":"\tconns, _ := c.getConnsAndFactory()\n\treturn len(conns)\n}\n\n// Stats returns stats for the pool.\nfunc (c *channelPool) Stats() (map[string]any, error) {\n\tconns, _ := c.getConnsAndFactory()\n\treturn map[string]any{\n\t\t\"idle\":                 len(conns),\n\t\t\"open_connections\":     c.nOpenConns,\n\t\t\"max_open_connections\": cap(conns),\n\t}, nil\n}\n\n// put puts the connection back to the pool. If the pool is full or closed,\n// conn is simply closed. A nil conn will be rejected.\nfunc (c *channelPool) put(conn net.Conn) error {\n\tif conn == nil {\n\t\treturn errors.New(\"connection is nil. rejecting\")\n\t}\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif c.conns == nil {\n\t\t// pool is closed, close passed connection\n\t\tatomic.AddInt64(&c.nOpenConns, -1)\n\t\treturn conn.Close()\n\t}\n\n\t// put the resource back into the pool. If the pool is full, this will\n\t// block and the default case will be executed.\n\tselect {\n\tcase c.conns <- conn:\n\t\treturn nil\n\tdefault:\n\t\t// pool is full, close passed connection\n\t\tatomic.AddInt64(&c.nOpenConns, -1)","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/rqlite/rqlite/blob/7586a4d1bdbd9a5a80021664c5a863cd850adb60/tcp/pool/channel.go#L104-L140","documentation":"Generic defensive guard in the TCP connection pool's put(): it rejects returning a nil net.Conn to the pool instead of panicking later on a nil dereference. Fires only from a caller bug — putting a nil connection that was never established or was already consumed.","triggerScenarios":"Calling put(conn) with a nil net.Conn, typically from Close() when iterating pooled connections, or from code paths that retrieve nothing but still call put.","commonSituations":"Pool teardown code that calls put on already-closed/zero-value connections; bugs in wrapper code that lost the real connection.","solutions":["Ensure only non-nil connections obtained via Get() are put back","Guard call sites: skip put when the connection is nil","On pool Close, close conns directly instead of routing through put"],"exampleFix":"// before\np.put(conn) // conn may be nil\n// after\nif conn != nil {\n  p.put(conn)\n}","handlingStrategy":"validation","validationCode":"if conn == nil {\n  return errors.New(\"cannot put nil connection into pool\")\n}","typeGuard":"func putIfNotNil(p *pool.ChannelPool, c net.Conn) {\n  if c != nil { p.Put(c) }\n}","tryCatchPattern":"if err := p.Put(conn); err != nil {\n  log.Printf(\"put rejected: %v\", err)\n}","preventionTips":["Only return conns obtained from Get() to the pool","Skip put for nil/already-closed conns","Close lost connections instead of pooling them"],"tags":["golang","tcp","connection-pool","nil"],"backgroundTag":"nil-argument","analyzedSha":"7586a4d1bdbd9a5a80021664c5a863cd850adb60","analyzedAt":"2026-09-03T07:03:02.260Z","contentChangedAt":"2026-09-03T07:03:02.260Z","schemaVersion":2},"datasetVersion":"2026-09-10T12:17:11.382Z"}