pydantic/monty · error

gather item is not a Coroutine, ExternalFuture, or GatherFut

Error message

gather item is not a Coroutine, ExternalFuture, or GatherFuture

What it means

When committing a gather, each item must be pollable: a Coroutine, ExternalFuture, or GatherFuture. If an item is anything else, this panic fires — the gather machinery only accepts awaitable-like values, so encountering another type means the earlier awaitability check or conversion is missing or heap state is corrupted.

Source

Thrown at crates/monty/src/bytecode/vm/async_exec.rs:268

                }
                HeapReadOutput::GatherFuture(child_gather) => {
                    if let Some(value) = poll_settled_gather(&child_gather, self.heap)? {
                        Poll::Ready(value)
                    } else {
                        drop(child_gather);
                        // Both the inc_ref and the awaiter that owns it are
                        // handed to the nested frame, which releases them when
                        // it settles; until then nothing is committed for this
                        // slot, so an error above needs no cleanup here.
                        self.heap.inc_ref(gather_id);
                        let sub_awaiter = Awaiter::GatherSlot {
                            gather: gather_id,
                            source: item_id,
                        };
                        return Ok(Some(self.open_gather_commit(item_id, sub_awaiter, Some(idx))));
                    }
                }
                _ => panic!("gather item is not a Coroutine, ExternalFuture, or GatherFuture"),
            };

            match poll {
                Poll::Ready(value) => frame.results[idx] = Some(value),
                Poll::Pending => {
                    frame.pending_children.insert(item_id, smallvec![idx]);
                }
            }
            frame.next += 1;
        }

        Ok(None)
    }

    /// Settles a fully-committed frame.
    ///
    /// With nothing left in flight the gather goes straight to `Completed` with
    /// its result list (this covers the empty `gather()` too); otherwise it

View on GitHub (pinned to adc986b362)

Solutions

  1. Verify gather items are awaited-eligible before constructing results (mirroring CPython's `An asyncio.Future, a coroutine or an awaitable is required` TypeError)
  2. File a bug with the reproducing async snippet if hit from valid Python code
  3. Run with --features memory-model-checks to hunt the underlying heap/refcount bug

Example fix

// before
items = gather([1, 2])  # non-awaitables must be rejected earlier

// after
items = gather([coro1, coro2])  # only awaitables passed to gather
Defensive patterns

Strategy: validation

Validate before calling

# user-side equivalent guard in Python
items = [it if asyncio.iscoroutine(it) or hasattr(it, '__await__') else ensure_future(it) for it in items]

Prevention

When it happens

Trigger: Internal: a gather() call contains an item that passed earlier validation but by commit time is not Coroutine/ExternalFuture/GatherFuture — e.g. an item replaced on the heap mid-commit, or a code path (like gather over a resolved non-awaitable) that skipped validation.

Common situations: Interpreter development on async_exec.rs; a gather fed results that were prematurely resolved or overwritten; heap corruption from a refcount bug.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13). Data as JSON: /api/errors/2510a8d7c326d16c. Report an issue: GitHub.