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

  1. Sanitize the DSL tree before submission: convert datetime -> isoformat, set -> list, Decimal -> float/str, NaN/Inf -> None
  2. Round-trip locally with json.loads(json.dumps(dsl, allow_nan=False)) to reproduce exactly what the server rejects
  3. Add a custom default= handler to any json.dumps used to build the DSL
  4. 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

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


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