Skip to content

Python: quickstart snippet fails #326

Description

@LucLonjon

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 pingPONG)

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions