-
Notifications
You must be signed in to change notification settings - Fork 72
Description
Describe the bug
The SurrealDB Python client throws an IndexError: list index out of range when attempting to parse duration type fields returned from queries. This occurs in the CBOR decoder's tag_decoder function when processing duration values.
Steps to reproduce
Steps to Reproduce
- Create a table with a
durationfield:
import asyncio
from surrealdb import AsyncSurreal
async def reproduce():
db = AsyncSurreal("ws://localhost:8000/rpc")
await db.signin({"username": "root", "password": "root"})
await db.use("test", "test")
# Create record with duration
await db.query("""
CREATE test_table SET
name = "Test",
event_duration = 2h;
""")
# Try to query it back - THIS FAILS
result = await db.query("SELECT * FROM test_table;")
print(result)
await db.close()
asyncio.run(reproduce())- Run the script
Expected behaviour
Expected Behavior
The query should successfully return records containing duration fields, with duration values properly parsed into Python Duration objects.
# Expected output
[{'name': 'Test', 'event_duration': Duration(...)}]Actual Behavior
The query raises an IndexError:
Traceback (most recent call last):
File "test.py", line 14, in reproduce
result = await db.query("SELECT * FROM test_table;")
File "/usr/local/lib/python3.13/site-packages/surrealdb/connections/async_ws.py", line 135, in query
response = await self._send(message, "query")
File "/usr/local/lib/python3.13/site-packages/surrealdb/connections/async_ws.py", line 61, in _send
response = decode(await self.socket.recv())
File "/usr/local/lib/python3.13/site-packages/surrealdb/data/cbor.py", line 141, in decode
return cbor2.loads(data, tag_hook=tag_decoder)
File "/usr/local/lib/python3.13/site-packages/surrealdb/data/cbor.py", line 120, in tag_decoder
return Duration.parse(tag.value[0], tag.value[1]) # Two numbers (s, ns)
~~~~~~~~~^^^
IndexError: list index out of range
Root Cause Analysis
The error occurs in /surrealdb/data/cbor.py at line 120:
def tag_decoder(tag: CBORTag) -> Any:
# ... other cases ...
case 14: # Duration
return Duration.parse(tag.value[0], tag.value[1]) # ← Assumes 2-element listThe code expects tag.value to be a list with exactly 2 elements [seconds, nanoseconds], but the actual CBOR-encoded duration from SurrealDB appears to have a different structure.
Workaround
The only current workaround is to use OMIT duration in all queries:
# This works
result = await db.query("SELECT * OMIT event_duration FROM test_table;")Impact
- Severity: High - Breaks all queries that return duration fields
- Scope: Affects both
query()andselect()methods - Production Impact: Causes cascading connection failures when pooling is used
Additional Context
This appears to be a mismatch between:
- How SurrealDB server encodes
durationvalues in CBOR - How the Python client expects to decode them
The CBOR tag handler assumes a 2-element array but receives something else. Adding debug logging to see the actual tag.value structure would help identify the correct parsing logic.
Suggested Fix
Add defensive checks and better error messages in the CBOR decoder:
case 14: # Duration
if not isinstance(tag.value, list):
raise ValueError(f"Expected list for Duration tag, got {type(tag.value)}: {tag.value}")
if len(tag.value) != 2:
raise ValueError(f"Expected 2-element list for Duration, got {len(tag.value)} elements: {tag.value}")
return Duration.parse(tag.value[0], tag.value[1])This would at least provide clearer error messages to diagnose the actual format being received.
Would appreciate any guidance on the correct CBOR format for duration values! Happy to test fixes and provide more debug info if needed.
SurrealDB version
2.3.10 for windows on x86_64
surrealdb.py version
0.3.2
Contact Details
No response
Is there an existing issue for this?
- I have searched the existing issues
Code of Conduct for repository surrealdb/surrealdb.py
- I agree to follow this project's Code of Conduct