Repository: cppa-cursor-browser
Reported: 2026-05-18
Severity: High
Summary
Navigating into a project by clicking an item in the "Projects with Conversations" table triggers a GET /api/workspaces/<id>/tabs request that crashes with a 500 error, making the project detail view completely inaccessible.
Reproduction Steps
- Run the app locally.
- Open the main dashboard.
- Click any project in the "Projects with Conversations" table.
- Observe a
500 response — the project detail view does not load.
Error Traceback
GET /api/workspaces/70dab26a3ac44c481f00d49e068473e7 HTTP/1.1" 200 -
Failed to get workspace tabs
Traceback (most recent call last):
File "api/workspaces.py", line 149, in get_workspace_tabs
payload, status = assemble_workspace_tabs(workspace_id, workspace_path, rules)
File "services/workspace_tabs.py", line 101, in assemble_workspace_tabs
bubble_obj = Bubble.from_dict(json.loads(row["value"]), bubble_id=bid)
TypeError: the JSON object must be str, bytes or bytearray, not NoneType
Root Cause
In services/workspace_tabs.py, the bubble-loading loop (lines 96–109) queries cursorDiskKV for all bubbleId: keys and immediately calls json.loads(row["value"]) without a None guard:
# services/workspace_tabs.py lines 96–109
for row in _safe_fetchall("SELECT key, value FROM cursorDiskKV WHERE key LIKE 'bubbleId:%'"):
parts = row["key"].split(":")
if len(parts) >= 3:
bid = parts[2]
try:
bubble_obj = Bubble.from_dict(json.loads(row["value"]), bubble_id=bid) # ← row["value"] can be None
bubble_map[bid] = bubble_obj.raw
except SchemaError as e:
print(f"Schema drift in bubble {bid}: {e}")
except (json.JSONDecodeError, ValueError): # ← TypeError is NOT caught
pass
When a cursorDiskKV row has a NULL value column, row["value"] resolves to None. json.loads(None) raises TypeError, which the current except clause does not handle. The exception propagates up through assemble_workspace_tabs and the API handler returns a 500.
Acceptance Criteria
Affected Files
services/workspace_tabs.py — lines 96–109 (bubble-loading loop)
api/workspaces.py — line 149 (call site)
Repository: cppa-cursor-browser
Reported: 2026-05-18
Severity: High
Summary
Navigating into a project by clicking an item in the "Projects with Conversations" table triggers a
GET /api/workspaces/<id>/tabsrequest that crashes with a500error, making the project detail view completely inaccessible.Reproduction Steps
500response — the project detail view does not load.Error Traceback
Root Cause
In
services/workspace_tabs.py, the bubble-loading loop (lines 96–109) queriescursorDiskKVfor allbubbleId:keys and immediately callsjson.loads(row["value"])without aNoneguard:When a
cursorDiskKVrow has aNULLvalue column,row["value"]resolves toNone.json.loads(None)raisesTypeError, which the currentexceptclause does not handle. The exception propagates up throughassemble_workspace_tabsand the API handler returns a500.Acceptance Criteria
row["value"] is Nonerows are skipped so a single NULL bubble row cannot crash the tabs endpointGET /api/workspaces/<id>/tabsreturns200(with the bad row silently skipped) for workspaces containing NULL bubble valuescursorDiskKVrow whosevalueisNULL/NoneAffected Files
services/workspace_tabs.py— lines 96–109 (bubble-loading loop)api/workspaces.py— line 149 (call site)