Summary
handle_infinite_request_xorq in buckaroo/server/xorq_loading.py:89 unconditionally puts traceback.format_exc() into the response payload's error_info field. The pandas-path handler in websocket_handler.py:243 correctly gates this behind _BUCKAROO_DEBUG and ships "Request failed" to clients in production. The xorq path doesn't — so any handler exception leaks internal source paths, line numbers, and stack frames to the WS client.
Reproduction
ws = await tornado.websocket.websocket_connect("ws://127.0.0.1:8700/ws/boston")
await ws.read_message() # initial state
ws.write_message(json.dumps({"type":"infinite_request","payload_args":{
"start":100, "end":50, "sourceName":"default", "origEnd":50}}))
resp = json.loads(await ws.read_message())
assert resp["error_info"].startswith("Traceback") # leaks
Observed on the boston xorq session — start=100, end=50 triggers an exception in the slice path, the response body carries:
'error_info': 'Traceback (most recent call last):\n File "/Users/paddy/code/buckaroo/buckaroo/server/xorq_loading.py", line 86 ...'
Diff
buckaroo/server/xorq_loading.py:89-91:
except Exception:
return ({"type": "infinite_resp", "key": payload_args, "data": [],
"length": 0, "error_info": traceback.format_exc()}, b"")
vs. the pandas WS handler at websocket_handler.py:241-245:
except Exception:
tb = traceback.format_exc()
log.error("infinite_request error session=%s: %s", self.session_id, tb)
self.write_message(json.dumps({"type": "infinite_resp", "key": payload_args, "data": [], "length": 0,
"error_info": tb if _BUCKAROO_DEBUG else "Request failed"}))
Note also: the xorq path doesn't log.error the traceback server-side, so an operator running a non-debug server has no record of the error. The pandas path logs it correctly.
Suggested fix
Mirror the pandas-side pattern in handle_infinite_request_xorq:
import os
_BUCKAROO_DEBUG = os.environ.get("BUCKAROO_DEBUG", "").lower() in ("1", "true")
...
except Exception:
tb = traceback.format_exc()
log = logging.getLogger("buckaroo.server.xorq_loading")
log.error("xorq infinite_request error: %s", tb)
return ({"type": "infinite_resp", "key": payload_args, "data": [],
"length": 0, "error_info": tb if _BUCKAROO_DEBUG else "Request failed"}, b"")
Even cleaner: lift the error-handling out of the per-backend handlers, let the WS handler's outer try/except handle it uniformly. That eliminates the duplication.
Test plan
- Failing-test commit: a Tornado WS test against a xorq session that triggers the exception path (e.g.
start > end), asserts error_info does NOT start with "Traceback" unless BUCKAROO_DEBUG=1 is set in env.
- Fix commit: gate by
_BUCKAROO_DEBUG, log the traceback server-side.
- Regression: existing tests pass; debug runs still see the full traceback.
🤖 Generated with Claude Code
Summary
handle_infinite_request_xorqinbuckaroo/server/xorq_loading.py:89unconditionally putstraceback.format_exc()into the response payload'serror_infofield. The pandas-path handler inwebsocket_handler.py:243correctly gates this behind_BUCKAROO_DEBUGand ships"Request failed"to clients in production. The xorq path doesn't — so any handler exception leaks internal source paths, line numbers, and stack frames to the WS client.Reproduction
Observed on the boston xorq session —
start=100, end=50triggers an exception in the slice path, the response body carries:Diff
buckaroo/server/xorq_loading.py:89-91:vs. the pandas WS handler at
websocket_handler.py:241-245:Note also: the xorq path doesn't
log.errorthe traceback server-side, so an operator running a non-debug server has no record of the error. The pandas path logs it correctly.Suggested fix
Mirror the pandas-side pattern in
handle_infinite_request_xorq:Even cleaner: lift the error-handling out of the per-backend handlers, let the WS handler's outer try/except handle it uniformly. That eliminates the duplication.
Test plan
start > end), assertserror_infodoes NOT start with"Traceback"unlessBUCKAROO_DEBUG=1is set in env._BUCKAROO_DEBUG, log the traceback server-side.🤖 Generated with Claude Code