infiniflow/ragflow · error · ValueError
ARGUMENT_ERROR
ARGUMENT_ERROR
Error message
DSL is not JSON-serializable.
What it means
Raised by CanvasReplicaService.normalize_dsl when normalize_chunker_dsl() produces a structure that json.dumps cannot serialize (raising inside the dumps/loads round-trip). This catches DSL objects containing non-JSON values — datetime objects, sets, custom class instances, NaN/Infinity floats — that would otherwise poison the Redis replica payload.
Source
Thrown at api/apps/services/canvas_replica_service.py:61
LOCK_RETRY_SLEEP_SECS = 0.2
@classmethod
def normalize_dsl(cls, dsl):
"""Normalize DSL to a JSON-serializable dict. Raise ValueError on invalid input."""
normalized = dsl
if isinstance(normalized, str):
try:
normalized = json.loads(normalized)
except Exception as e:
raise ValueError("Invalid DSL JSON string.") from e
if not isinstance(normalized, dict):
raise ValueError("DSL must be a JSON object.")
try:
return json.loads(json.dumps(normalize_chunker_dsl(normalized), ensure_ascii=False))
except Exception as e:
raise ValueError("DSL is not JSON-serializable.") from e
@classmethod
def _replica_key(cls, canvas_id: str, tenant_id: str, runtime_user_id: str) -> str:
return f"{cls.REPLICA_KEY_PREFIX}:{canvas_id}:{tenant_id}:{runtime_user_id}"
@classmethod
def _lock_key(cls, canvas_id: str, tenant_id: str, runtime_user_id: str) -> str:
return f"{cls.LOCK_KEY_PREFIX}:{canvas_id}:{tenant_id}:{runtime_user_id}"
@classmethod
def _read_payload(cls, replica_key: str):
"""Read replica payload from Redis; return None on missing/invalid content."""
cache_blob = REDIS_CONN.get(replica_key)
if not cache_blob:
return None
try:
payload = json.loads(cache_blob)
if not isinstance(payload, dict):View on GitHub (pinned to 554fb1133a)
Solutions
- Sanitize the DSL tree before submission: convert datetime -> isoformat, set -> list, Decimal -> float/str, NaN/Inf -> None
- Round-trip locally with json.loads(json.dumps(dsl, allow_nan=False)) to reproduce exactly what the server rejects
- Add a custom default= handler to any json.dumps used to build the DSL
- If NaNs come from numpy, cast with float() and replace non-finite values before serializing
Example fix
# before
dsl["created_at"] = row.created_at # datetime object
dsl["ids"] = {"a", "b"} # set
# after
dsl["created_at"] = row.created_at.isoformat()
dsl["ids"] = sorted({"a", "b"})
json.dumps(dsl, allow_nan=False, default=str) Defensive patterns
Strategy: validation
Validate before calling
import json, math
def json_safe(dsl):
json.loads(json.dumps(dsl, allow_nan=False, default=_reject))
return dsl
def _reject(o):
raise TypeError(f"non-JSON value: {o!r}") Type guard
def is_json_serializable(v) -> bool:
try:
json.dumps(v, allow_nan=False)
return True
except (TypeError, ValueError):
return False Try / catch
try:
service.normalize_dsl(dsl)
except ValueError as e:
if "serializable" in str(e):
dsl = sanitize(dsl) # datetimes->isoformat, sets->list, NaN->None
service.normalize_dsl(dsl) Prevention
- Convert datetime/Decimal/set/numpy values at the boundary where DSL is built
- Use allow_nan=False in every dumps so bad floats fail locally first
- Never insert ORM model instances into DSL params; extract primitives
When it happens
Trigger: Submitting a canvas DSL built programmatically where component params contain Python-specific objects (set, datetime, Decimal, numpy types, float('nan')); a migration path in normalize_chunker_dsl that emits such a value; NaN floats, which json.dumps allows by default but json.loads(strict) round-trip can reject.
Common situations: Building DSL from ORM rows or API responses without converting datetimes to ISO strings; pandas/numpy values leaking into params; old DSL files with Infinity/NaN literals produced by allow_nan=True serializers.
Related errors
- 101
- {component_name}: {e}
- main() must be defined or exported.
- main() must return a value. Use null for an empty result.
- main() returned a non-JSON-serializable value.
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/2caa5211d474bace.
Report an issue: GitHub.