-
Notifications
You must be signed in to change notification settings - Fork 11
Connection Fails with WSServerHandshakeError 505 for Regional URL (*.region.turso.io), but Turso CLI Succeeds #34
Description
1. Environment Details:
- Operating System: macOS (Sequoia 15.4.1)
- Python Version: 3.11
- libsql-client Version: 0.3.1
- sqlalchemy-libsql Version: 0.1.0
- aiohttp Version: 3.11.18
2. Problem Description:
I am trying to connect to a Turso database from a Python application running in Docker using libsql-client (and sqlalchemy-libsql). The connection consistently fails during the WebSocket handshake phase with an aiohttp.client_exceptions.WSServerHandshakeError: 505, message='Invalid response status'.
Using the official turso CLI (v1.0.9) to connect to the exact same database URL works perfectly fine.
3. Turso Database URL:
- The exact hostname format provided by Turso that I am using is: chiro-app-wrtirado.aws-us-west-2.turso.io.
- And the URL format I used in my Python tests (token hidden) is: libsql://chiro-app-wrtirado.aws-us-west-2.turso.io?authToken=[REDACTED_TOKEN]
- I also tried wss://... explicitly with the same result, as well.
- SQLAlchemy connection string was: sqlite+libsql://chiro-app-wrtirado.aws-us-west-2.turso.io?authToken=[REDACTED_TOKEN]&secure=true
4. Evidence of Success (Turso CLI):
Connected to chiro-app at libsql://chiro-app-wrtirado.aws-us-west-2.turso.io
Welcome to Turso SQL shell!
Type ".quit" to exit the shell and ".help" to list all available commands.
→ SELECT 1;
1
1
→ .tables
→ .quit
5. Evidence of Failure (Python Client):
I have been using this file to test the Turso connection:
# test_turso_connection.py
import os
import libsql_client
# Ensure LIBSQL_AUTH_TOKEN is available as an environment variable
auth_token = os.environ.get("LIBSQL_AUTH_TOKEN")
if not auth_token:
print("Error: LIBSQL_AUTH_TOKEN environment variable not set.")
exit(1)
# Using the libsql scheme directly as Turso provides, with authToken in the URL query
# The sqlalchemy-libsql dialect would normally handle the sqlite+libsql part.
# Here we test the underlying client with what it typically expects for a remote server.
turso_db_hostname = "chiro-app-wrtirado.aws-us-west-2.turso.io"
# client_url = f"libsql://{turso_db_hostname}?authToken={auth_token}"
client_url = f"wss://{turso_db_hostname}?authToken={auth_token}"
print(
f"Attempting to connect to: {client_url} (using libsql_client.create_client_sync)"
)
try:
# create_client_sync is used because SQLAlchemy operates synchronously
# and its DBAPI drivers are expected to be synchronous.
client = libsql_client.create_client_sync(url=client_url)
print("Client created successfully.")
# Test with a simple query to ensure the connection is usable
print("Attempting to execute a simple query (SELECT 1)...")
rs = client.execute("SELECT 1")
print(
f"Query executed successfully. Result rows: {rs.rows}, Column names: {rs.columns}"
)
if rs.rows and len(rs.rows) > 0 and rs.rows[0][0] == 1:
print("Connection and basic query successful!")
else:
print(
"Query executed, but result was not as expected (expected [[1]] or similar)."
)
except Exception as e:
print(f"Connection or query failed: {e}")
print(f"Type of exception: {type(e)}")
if hasattr(e, "args"):
print(f"Exception args: {e.args}")
# If there are nested exceptions, like in the Alembic traceback
if e.__cause__:
print(f"Cause: {e.__cause__}")
print(f"Type of cause: {type(e.__cause__)}")
if e.__context__:
print(f"Context: {e.__context__}")
print(f"Type of context: {type(e.__context__)}")
finally:
if "client" in locals() and client:
client.close()
print("Client closed.")
With containers up and running, I am using the following command: docker-compose exec -e LIBSQL_AUTH_TOKEN="MY_TOKEN" api python test_turso_connection.py
, and this is the full traceback result I get:
Will@Williams-MacBook-Pro-2 cursor-chiro-app % docker-compose exec -e LIBSQL_AUTH_TOKEN="REDACTED_TOKEN" api python test_turso_connection.py
Attempting to connect to: wss://chiro-app-wrtirado.aws-us-west-2.turso.io?authToken=REDACTED_TOKEN (using libsql_client.create_client_sync)
Client created successfully.
Attempting to execute a simple query (SELECT 1)...
Connection or query failed: 505, message='Invalid response status', url='wss://chiro-app-wrtirado.aws-us-west-2.turso.io'
Type of exception: <class 'aiohttp.client_exceptions.WSServerHandshakeError'>
Exception args: (RequestInfo(url=URL('wss://chiro-app-wrtirado.aws-us-west-2.turso.io'), method='GET', headers=<CIMultiDictProxy('Host': 'chiro-app-wrtirado.aws-us-west-2.turso.io', 'Upgrade': 'websocket', 'Connection': 'Upgrade', 'Sec-WebSocket-Version': '13', 'Sec-WebSocket-Key': 'TIyIIaSaiBhP8ML1o5nSiw==', 'Sec-WebSocket-Protocol': 'hrana2', 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Python/3.11 aiohttp/3.11.18')>, real_url=URL('wss://chiro-app-wrtirado.aws-us-west-2.turso.io')), ())
Client closed.
6. What I Have Tried:
I confirmed the database is healthy via the Turso dashboard.
I tried both libsql:// and wss:// schemes directly in the test script.
I tried adding &secure=true to the SQLAlchemy URL.
7. Question:
Is this a known issue, particularly with Turso endpoints having regional hostnames (*.region.turso.io), or could it be a bug in libsql-client's interaction with aiohttp for these specific WebSocket connections?