denoland/deno · error · TypeError

${openContext} failed to close iterator because the return()

Error message

${openContext} failed to close iterator because the return() method did not return an object, but ${type(returnPromiseResult)}.

What it means

When a WebIDL async-sequence consumer closes the iterator early (break, error, abort), it looks up the iterator's optional return(); if present, the awaited result must be an Object. Otherwise this TypeError is thrown, including the API context (openContext) and the offending type name.

Source

Thrown at ext/webidl/00_webidl.js:1183

              opts,
            );

            return { done: false, value: iterValue };
          },
          // https://webidl.spec.whatwg.org/#async-iterator-close
          async return(reason) {
            const returnMethod = getMethod(asyncIterator, "return");
            if (returnMethod === undefined) {
              return undefined;
            }

            const returnPromiseResult = await FunctionPrototypeCall(
              returnMethod,
              asyncIterator,
              reason,
            );
            if (type(returnPromiseResult) !== "Object") {
              throw new TypeError(
                `${openContext} failed to close iterator because the return() method did not return an object, but ${
                  type(returnPromiseResult)
                }.`,
              );
            }

            return undefined;
          },
          [SymbolAsyncIterator]() {
            return this;
          },
        };
      },
      // Allow for-await-of over the converted async sequence directly.
      [SymbolAsyncIterator]() {
        return this.open(context);
      },
    };

View on GitHub (pinned to f7822238ca)

Solutions

  1. Return { done: true } from return()
  2. Or omit return() - an absent method means no close is attempted
  3. When delegating, return the inner iterator's result object

Example fix

// before
async return() { await cleanup(); } // resolves to undefined

// after
async return() { await cleanup(); return { done: true }; }
Defensive patterns

Strategy: validation

Type guard

interface CloseableAsyncIterator<T> extends AsyncIterator<T> {
  return?(value?: any): Promise<IteratorResult<T>>;
}
// Typing return() as Promise<IteratorResult<T>> prevents resolving to primitives.

Prevention

When it happens

Trigger: An async-iterable fetch body whose return() resolves to a non-object - return() { } (undefined), async return() { await cleanup() } where cleanup returns a string, or return: async () => null - followed by early termination (AbortController, downstream error, read limit).

Common situations: Cleanup hooks that forget to return a value; returning a boolean success flag from return(); return() that resolves to a resource handle number.

Related errors


AI-assisted analysis of denoland/deno@f7822238ca (2026-08-20). Data as JSON: /api/errors/421918e90b75f8fb. Report an issue: GitHub.