usestrix/strix · warning · ValueError
Updates must be valid JSON
Error message
Updates must be valid JSON
What it means
_normalize_bulk_updates accepts the updates argument either as a JSON string or as already-decoded data. If a non-empty string is passed, it json.loads it; a JSONDecodeError is re-raised as ValueError('Updates must be valid JSON'). Empty/whitespace strings return [] (no updates) rather than erroring.
Source
Thrown at strix/tools/todo/tools.py:164
return [str(item).strip() for item in data if str(item).strip()]
return [str(data).strip()]
if isinstance(raw_ids, list):
return [str(item).strip() for item in raw_ids if str(item).strip()]
return [str(raw_ids).strip()]
def _normalize_bulk_updates(raw_updates: Any) -> list[dict[str, Any]]:
if raw_updates is None:
return []
data: Any = raw_updates
if isinstance(raw_updates, str):
stripped = raw_updates.strip()
if not stripped:
return []
try:
data = json.loads(stripped)
except json.JSONDecodeError as e:
raise ValueError("Updates must be valid JSON") from e
if isinstance(data, dict):
data = [data]
if not isinstance(data, list):
raise TypeError("Updates must be a list of update objects")
normalized: list[dict[str, Any]] = []
for item in data:
if not isinstance(item, dict):
raise TypeError("Each update must be an object with todo_id")
todo_id = item.get("todo_id") or item.get("id")
if not todo_id:
raise ValueError("Each update must include 'todo_id'")
normalized.append(
{
"todo_id": str(todo_id).strip(),
"title": item.get("title"),
"description": item.get("description"),View on GitHub (pinned to 8551339130)
Solutions
- Pass strict JSON: double quotes, no trailing commas, no comments — or better, pass a decoded list/dict directly instead of a string.
- Validate first: json.loads(s) in a try/except before calling the tool.
- For LLM callers, instruct JSON-only output and consider json mode / structured outputs.
Example fix
# before (Python-literal quotes — invalid JSON)
bulk_update_todos(updates="[{'id': 't1', 'status': 'done'}]")
# after
bulk_update_todos(updates='[{"id": "t1", "status": "done"}]')
# or pass decoded data directly:
bulk_update_todos(updates=[{"id": "t1", "status": "done"}]) Defensive patterns
Strategy: validation
Validate before calling
import json
def parse_updates_arg(updates):
if isinstance(updates, str):
s = updates.strip()
if not s:
return []
return json.loads(s) # raises here with a precise JSON error
return updates
# pre-check before calling the tool
json.loads(updates_str) # will raise json.JSONDecodeError, not the tool's ValueError Try / catch
try:
bulk_update_todos(updates=raw)
except ValueError as exc:
if "valid JSON" in str(exc):
raw = json.dumps(fix_python_literals(raw)) # repair quotes/commas, retry once
bulk_update_todos(updates=raw)
else:
raise Prevention
- Pass decoded Python objects instead of JSON strings when your caller can.
- Use strict JSON: double quotes, no trailing commas, no comments.
- For LLM callers, enable structured/JSON output modes and validate before the call.
When it happens
Trigger: Calling a bulk todo update tool with a string that isn't valid JSON — trailing commas, single quotes, Python-repr dicts ({'id': 't1'}), or truncated JSON from an LLM. Only raises for non-empty strings that fail to parse.
Common situations: LLM emitting Python-literal style dicts instead of JSON; hand-typed JSON with comments; copy-paste losing a closing brace; passing an already-decoded object wrapped in str().
Related errors
- Invalid priority. Must be one of: {', '.join(VALID_PRIORITIE
- Updates must be a list of update objects
- Each update must be an object with todo_id
- Failed to calculate CVSS for validated vector: {vector}
- Todos must be provided as a list, dict, or JSON string
AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15).
Data as JSON: /api/errors/adcba3b63c41d5b9.
Report an issue: GitHub.