apache/beam · error · Error

Cannot be called outside of a DoFn's process method.

Error message

Cannot be called outside of a DoFn's process method.

What it means

ParDoLookupParam.lookup() requires a side-input provider that is only injected while a DoFn's process method runs. If provider is undefined — meaning lookup() was called outside element processing — it throws 'Cannot be called outside of a DoFn's process method.'

Source

Thrown at sdks/typescript/src/apache_beam/transforms/pardo.ts:311

 */
export class ParDoParam {
  // Provided externally.
  /** @internal */
  protected provider: ParamProvider | undefined;

  /** @internal */
  constructor(readonly parDoParamName: string) {}
}

/**
 * At runtime, one can invoke the special `lookup` method to retrieve the
 * relevant value associated with the currently-being-processed element.
 */
export class ParDoLookupParam<T> extends ParDoParam {
  // TODO: Nameing "get" seems to be special.
  lookup(): T {
    if (this.provider === undefined) {
      throw new Error("Cannot be called outside of a DoFn's process method.");
    }

    return this.provider.lookup(this);
  }
}

/**
 * At runtime, one can invoke the special `update` method to update the
 * relevant value associated with the currently-being-processed element.
 */
export class ParDoUpdateParam<T> extends ParDoParam {
  update(value: T): void {
    if (this.provider === undefined) {
      throw new Error("Cannot be called outside of a DoFn's process method.");
    }

    this.provider.update(this, value);
  }

View on GitHub (pinned to 12126d8942)

Solutions

  1. Move the lookup() call inside the DoFn's processElement/process method where the provider is bound.
  2. For tests, construct a ParDoLookupParam with a mock provider set instead of relying on runtime injection.
  3. Use startBundle/process-scoped state to store values you need outside process.

Example fix

// before
class MyFn extends DoFn {
  setup() { this.value = this.sideInput.lookup(); } // throws
}
// after
class MyFn extends DoFn {
  processElement(e) { const v = this.sideInput.lookup(); ... }
}
Defensive patterns

Strategy: validation

Validate before calling

function canLookup(param: ParDoLookupParam<T>) { return param.provider !== undefined; }
if (!canLookup(sideInput)) console.warn("lookup() only valid inside processElement");

Type guard

function inProcessContext<T>(p: ParDoLookupParam<T>): p is ParDoLookupParam<T> & { provider: NonNullable<ParDoLookupParam<T>["provider"]> } {
  return p.provider !== undefined;
}

Try / catch

try {
  const v = this.sideInput.lookup();
} catch (e) {
  if (e.message.includes("outside of a DoFn's process method")) {
    throw new Error("Move side-input lookup into processElement");
  } else throw e;
}

Prevention

When it happens

Trigger: Calling sideInput.lookup() (a ParDoLookupParam captured in the DoFn) from setup/teardown/startBundle/finishBundle, from the constructor, or from plain application code outside the pipeline.

Common situations: Developers caching the side-input object and calling lookup() at class level or in unit tests without executing through the DoFn process pipeline context.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/d4db585005df3820. Report an issue: GitHub.