dbt-labs/dbt-core · error · InvalidOperation

Failed to convert value to JSON: {err}

Error message

Failed to convert value to JSON: {err}

What it means

The tojson Jinja filter serializes a value to a Python-style JSON string. When the underlying conversion to a serde_json value fails and no default argument was supplied, this InvalidOperation error is raised carrying the serialization error message. With a default provided, the default is returned instead.

Source

Thrown at crates/dbt-jinja-utils/src/functions/base.rs:477

        Ok(mut json_value) => {
            if sort_keys && let Some(obj) = json_value.as_object_mut() {
                // Sort the keys using BTreeMap
                let sorted: serde_json::Map<String, serde_json::Value> = obj
                    .iter()
                    .collect::<BTreeMap<_, _>>()
                    .into_iter()
                    .map(|(k, v)| (k.clone(), v.clone()))
                    .collect();
                json_value = serde_json::Value::Object(sorted);
            }
            // Use Python-style formatting (space after colon)
            let json_str =
                to_json_string_python_style(&json_value).unwrap_or_else(|_| "{}".to_string());
            Ok(Value::from_safe_string(json_str))
        }
        Err(err) => match default {
            Some(default_value) => Ok(default_value.clone()),
            None => Err(Error::new(
                ErrorKind::InvalidOperation,
                format!("Failed to convert value to JSON: {err}"),
            )),
        },
    }
}

/// Deserialize a YAML string into a Python object primitive.
///
/// ```python
/// def fromyaml(value: str, default: Any = None) -> Any:
///     """The fromyaml context method can be used to deserialize a yaml string
///     into a Python object primitive, eg. a `dict` or `list`.
///
///     :param value: The yaml string to deserialize
///     :param default: A default value to return if the `string` argument
///         cannot be deserialized (optional)
///

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Pass a default: {{ value | tojson(default='{}') }} to fall back on serialization failure.
  2. Ensure the value is plain data (dict/list/scalar) before piping to tojson.
  3. Guard with is defined / is mapping tests before serializing.
  4. If serializing a dbt relation or object, serialize its serializable attributes instead.

Example fix

// before
{{ config_obj | tojson }}
// after
{{ config_obj | tojson(default='{}') }}
Defensive patterns

Strategy: fallback

Validate before calling

{% if value is defined and (value is mapping or value is sequence or value is string) %}...{% endif %}

Try / catch

{{ value | tojson(default='{}') }} // graceful fallback inside the filter

Prevention

When it happens

Trigger: {{ value | tojson }} where value is not JSON-serializable (cyclic structure, non-string map keys that can't convert, or an opaque minijinja value), and no default parameter is passed.

Common situations: Serializing undefined or exotic objects from context, dumping results of macros that return unsupported types, or templates that relied on implicit coercion.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07). Data as JSON: /api/errors/84b545ffaf3c1f48. Report an issue: GitHub.