Python quickstart snippet uses an outdated client-construction pattern, causes a misleading connection failure
Summary
The Python quickstart snippet fails at runtime. Calling client.ping() immediately raises:
Connection failed: initializer for ctype 'void *' must be a cdata pointer, not NoneType
Future exception was never retrieved
future: <Future finished exception=ClosingError('No error message provided')>
glide_shared.exceptions.ClosingError: No error message provided
This reproduces consistently and is not environment-specific:
- Python 3.13 and 3.14
- valkey-glide 2.5.1 and 2.5.2rc1
- With and without a debugger attached (VS Code / debugpy)
- Inside a dev container, with the target server independently confirmed reachable (
redis-cli -h <host> -p 6379 ping → PONG)
Because the error surfaces as a low-level FFI/ctypes failure rather than anything referencing client setup, it strongly suggests an environment or native-library problem. That sent us down a long, unproductive debugging path — checking Python 3.14 support, client version, debugger fork-safety, and container pipe/FD restrictions — before finding that none of those were the actual cause.
Root Cause
The quickstart snippet constructs the client directly:
client = GlideClient([NodeAddress("host.docker.internal", 6379)])
This is not the current client-creation API. The correct pattern builds a configuration object and creates the client through the async factory method:
config = GlideClientConfiguration(addresses)
client = await GlideClient.create(config)
GlideClient.create() is where the actual async connection handshake happens, and it's what populates the internal Rust-side connection handle. Calling the bare constructor instead returns a Python object that looks valid, but its internal handle was never established. The first command call (ping()) then reaches across the FFI boundary for a handle that doesn't exist, and the underlying cffi call receives None where it expects a real pointer — producing the reported error.
The resulting error message gives no indication that the actual problem is how the client was constructed, which makes this very easy to misdiagnose as a platform/version/environment issue rather than an application-code issue.
Suggested Fix
1. Update the quickstart snippet (and any other examples still using the direct constructor) to the current async factory pattern:
import asyncio
from glide import GlideClient, GlideClientConfiguration, NodeAddress
async def main():
addresses = [NodeAddress("host.docker.internal", 6379)]
config = GlideClientConfiguration(addresses)
client = await GlideClient.create(config)
try:
response = await client.ping()
print(f"Connected! Server responded: {response}")
except Exception as e:
print(f"Connection failed: {e}")
finally:
await client.close()
if __name__ == "__main__":
asyncio.run(main())
2. Consider a defensive improvement on the client side (separate from the docs fix, worth a note/cross-link to the client repo): calling GlideClient(...) directly could raise a clear, immediate error (e.g. "Use await GlideClient.create(config) instead of the constructor") rather than producing a superficially valid object that fails opaquely on the first command. That would prevent this exact multi-hour misdiagnosis for the next person who copies the outdated pattern from an older example, blog post, or cached search result.
Python quickstart snippet uses an outdated client-construction pattern, causes a misleading connection failure
Summary
The Python quickstart snippet fails at runtime. Calling
client.ping()immediately raises:This reproduces consistently and is not environment-specific:
redis-cli -h <host> -p 6379 ping→PONG)Because the error surfaces as a low-level FFI/ctypes failure rather than anything referencing client setup, it strongly suggests an environment or native-library problem. That sent us down a long, unproductive debugging path — checking Python 3.14 support, client version, debugger fork-safety, and container pipe/FD restrictions — before finding that none of those were the actual cause.
Root Cause
The quickstart snippet constructs the client directly:
This is not the current client-creation API. The correct pattern builds a configuration object and creates the client through the async factory method:
GlideClient.create()is where the actual async connection handshake happens, and it's what populates the internal Rust-side connection handle. Calling the bare constructor instead returns a Python object that looks valid, but its internal handle was never established. The first command call (ping()) then reaches across the FFI boundary for a handle that doesn't exist, and the underlying cffi call receivesNonewhere it expects a real pointer — producing the reported error.The resulting error message gives no indication that the actual problem is how the client was constructed, which makes this very easy to misdiagnose as a platform/version/environment issue rather than an application-code issue.
Suggested Fix
1. Update the quickstart snippet (and any other examples still using the direct constructor) to the current async factory pattern:
2. Consider a defensive improvement on the client side (separate from the docs fix, worth a note/cross-link to the client repo): calling
GlideClient(...)directly could raise a clear, immediate error (e.g. "Useawait GlideClient.create(config)instead of the constructor") rather than producing a superficially valid object that fails opaquely on the first command. That would prevent this exact multi-hour misdiagnosis for the next person who copies the outdated pattern from an older example, blog post, or cached search result.