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}"

    @classmethod

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Serialize the DSL with json.dumps(dsl) (or pass the dict directly) before sending
  2. Validate the string locally with json.loads() before the API call
  3. If the string came out of Redis, flush the canvas:replica:* key so a fresh replica is bootstrapped from the DB DSL
  4. 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

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


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/245cfcae9d5bc40a. Report an issue: GitHub.