apache/beam · critical · Error

Method expand has not been implemented.

Error message

Method expand has not been implemented.

What it means

AsyncPTransformClass.expandAsync is the abstract expansion method; the base class throws this error if it is invoked without being overridden. It means a custom PTransform subclass (or one reaching this base implementation) never implemented expandAsync, so the pipeline cannot expand the transform into primitive operations.

Solutions

  1. Override expandAsync(input) in your subclass and return the expanded PValue.
  2. If your transform is synchronous, extend PTransformClass and implement expand() instead.
  3. Do not instantiate AsyncPTransformClass directly — it is an abstract base.

Example fix

// before
class MyTransform extends AsyncPTransformClass<PCollection<string>, PCollection<number>> {}

// after
class MyTransform extends AsyncPTransformClass<PCollection<string>, PCollection<number>> {
  async expandAsync(input: PCollection<string>): Promise<PCollection<number>> {
    return input.map(s => s.length).withName("lengths");
  }
}
Defensive patterns

Strategy: type-guard

Validate before calling

function overridesExpandAsync(t: object): boolean {
  return t.constructor !== AsyncPTransformClass &&
    t['expandAsync'] !== AsyncPTransformClass.prototype.expandAsync;
}

Type guard

const implementsExpandAsync = (t: unknown): t is { expandAsync(i: never): Promise<unknown> } =>
  typeof (t as any)?.expandAsync === 'function' &&
  (t as any).expandAsync !== AsyncPTransformClass.prototype.expandAsync;

Try / catch

try {
  await pipeline.runInternal();
} catch (e) {
  if (/Method expand has not been implemented/.test(String(e)))
    throw new Error('Your custom PTransform must override expandAsync');
  throw e;
}

Prevention

When it happens

Trigger: Extending AsyncPTransformClass and calling expandAsync (directly or via expandInternalAsync) without overriding expandAsync; instantiating the base class directly.

Common situations: Writing a custom composite transform and forgetting to implement the expansion method; renaming/refactoring that removed the override.

Related errors


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

Appendix: source

Thrown at sdks/typescript/src/apache_beam/transforms/transform.ts:96

// Note also that the requirement for both a synchronous and asynchronous
// variant is imposed by javascript, and is not necessarily relevant in other
// languages (especially if an asynchronous call can be turned into a blocking
// call rather than forcing the asynchronous nature all the way up the call
// hierarchy).

/** @internal */
export class AsyncPTransformClass<
  InputT extends PValue<any>,
  OutputT extends PValue<any>,
> {
  beamName: string | (() => string);

  constructor(name: string | (() => string) | null = null) {
    this.beamName = name || this.constructor.name;
  }

  async expandAsync(input: InputT): Promise<OutputT> {
    throw new Error("Method expand has not been implemented.");
  }

  async expandInternalAsync(
    input: InputT,
    pipeline: Pipeline,
    transformProto: runnerApi.PTransform,
  ): Promise<OutputT> {
    return this.expandAsync(input);
  }
}

/** @internal */
export class PTransformClass<
  InputT extends PValue<any>,
  OutputT extends PValue<any>,
> extends AsyncPTransformClass<InputT, OutputT> {
  expand(input: InputT): OutputT {
    throw new Error("Method expand has not been implemented.");

View on GitHub (pinned to 12126d8942)