#1942·outlines

DSL container types render bool as Python True/False, producing invalid JSON and diverging from the JSON-schema path

Author: ErenAta16Created Jul 20, 2026Updated Aug 31, 2026

What

Inside a container type built by the DSL (list[bool], dict[str, bool], tuple[bool, ...]), booleans render Python-style as True/False, while everything else in the same container is JSON-shaped: strings are JSON-quoted ("...") and the array/object brackets are JSON. The result is a string that is neither valid JSON nor consistent with the JSON-schema path, which emits lowercase true/false for the same type.

To be clear up front: the standalone types.boolean being Python-style is intentional. tests/types/test_custom_types.py explicitly asserts "True"/"False" match and "true" does not, so I'm not suggesting changing that type on its own. This is specifically about what happens when bool is nested inside a DSL container, where the surrounding output is otherwise JSON.

Reproduction

python
import re
from typing import List
from pydantic import BaseModel
from outlines.types.dsl import python_types_to_terms, to_regex, JsonSchema

# DSL container path
dsl_pattern = to_regex(python_types_to_terms(List[bool]))
print("DSL list[bool]:", dsl_pattern)
print("  matches [true] :", re.fullmatch(dsl_pattern, "[true]") is not None)   # False
print("  matches [True] :", re.fullmatch(dsl_pattern, "[True]") is not None)   # True

# JSON-schema path for the same type
class M(BaseModel):
    items: List[bool]

js_pattern = to_regex(JsonSchema(M))
print('  JSON-schema {"items": [true]} :', re.fullmatch(js_pattern, '{"items": [true]}') is not None)  # True
print('  JSON-schema {"items": [True]} :', re.fullmatch(js_pattern, '{"items": [True]}') is not None)  # False

Output:

DSL list[bool]: \[(((True|False))(,\ ((True|False)))*)?\]
  matches [true] : False
  matches [True] : True
  JSON-schema {"items": [true]} : True
  JSON-schema {"items": [True]} : False

Why this is inconsistent

Within one DSL container, string values come out JSON-quoted (_ensure_json_quoted wraps Literal["cat"] as "cat"), and the brackets are JSON arrays/objects, but booleans come out Python-style. So list[bool] generates [True], which json.loads rejects. The same logical type routed through a pydantic field (List[bool]) generates [true], which is valid JSON. So the choice of entry point silently changes whether the generated output parses as JSON, same class of divergence as #1938 (dataclass optionality) and #1940 (empty collections) but for boolean literals.

Possible direction

bool inside a container could be lowercased to true/false, in the same spot where strings already get JSON-quoted for containers (_ensure_json_quoted, or the _handle_list/_handle_dict/_handle_tuple element handling), leaving the standalone types.boolean untouched so its intentional Python-style behavior and its existing test still hold.

This is a design call rather than an obvious fix, since it depends on whether the DSL container output is meant to be strict JSON. Flagging it as an issue rather than sending a PR for that reason. Happy to open one scoped to the container path if that's the direction you'd want.

Environment

Reproduced against dottxt-ai/outlines main (current). Pure Python, no model/backend needed.