infiniflow/ragflow · error · ValueError
101
101
Error message
Invalid DSL JSON string.
What it means
Raised by CanvasReplicaService.normalize_dsl when the canvas DSL is provided as a string but json.loads fails to parse it. The replica service stores per-user canvas runtime copies in Redis and requires the DSL to be a plain JSON object, so any malformed JSON string (trailing commas, single quotes, truncated payloads, BOM) is rejected with ValueError before touching Redis.
Source
Thrown at api/apps/services/canvas_replica_service.py:53
"""
TTL_SECS = 3 * 60 * 60
REPLICA_KEY_PREFIX = "canvas:replica"
LOCK_KEY_PREFIX = "canvas:replica:lock"
LOCK_TIMEOUT_SECS = 10
LOCK_BLOCKING_TIMEOUT_SECS = 1
LOCK_RETRY_ATTEMPTS = 3
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}"
@classmethodView on GitHub (pinned to 554fb1133a)
Solutions
- Serialize the DSL with json.dumps(dsl) (or pass the dict directly) before sending
- Validate the string locally with json.loads() before the API call
- If the string came out of Redis, flush the canvas:replica:* key so a fresh replica is bootstrapped from the DB DSL
- Check for truncated payloads: log len(dsl_string) and compare with what the client sent
Example fix
# before dsl_str = str(canvas_dsl) # single quotes -> invalid JSON service.normalize_dsl(dsl_str) # after import json dsl_str = json.dumps(canvas_dsl) # valid JSON service.normalize_dsl(dsl_str)
Defensive patterns
Strategy: validation
Validate before calling
import json
def valid_dsl_json(dsl_str: str) -> bool:
try:
json.loads(dsl_str)
return True
except (json.JSONDecodeError, TypeError):
return False Type guard
def is_json_string(v) -> bool:
return isinstance(v, str) and _valid_dsl_json(v) Try / catch
try:
CanvasReplicaService.normalize_dsl(dsl)
except ValueError as e:
# message names the exact stage: invalid JSON / not an object / not serializable
logger.warning("DSL rejected: %s", e) Prevention
- Always build DSL strings with json.dumps, never str() or repr()
- Pass dicts directly when the API accepts objects
- Round-trip test locally: json.loads(json.dumps(dsl)) before sending
When it happens
Trigger: Calling a canvas run/save API with dsl as a string containing invalid JSON — e.g. Python-repr dicts with single quotes, truncated request bodies, or strings produced by str(dict) instead of json.dumps. Reached via _read_payload (corrupt Redis replica) or _build_payload (bad input DSL).
Common situations: Serializing a dict with str() or repr() instead of json.dumps(); hand-editing canvas DSL strings; truncated HTTP bodies behind proxies; a corrupted Redis replica entry (in which case _read_payload logs a warning and returns None instead).
Related errors
- ARGUMENT_ERROR
- flow.formatTypeError
- {component_name}: {e}
- 'str' object has no attribute 'get'
- JSON payload must be an object.
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/245cfcae9d5bc40a.
Report an issue: GitHub.