BabylonJS/Babylon.js · error

FrameGraph.addTask: Can't add the task "${task.name}" while

Error message

FrameGraph.addTask: Can't add the task "${task.name}" while another task is currently building (task: ${this._currentProcessedTask.name}).

What it means

FrameGraph.addTask() refuses to add a new task while another task is inside its build/record phase (this._currentProcessedTask !== null). Frame graph tasks call addTask/record recursively or from callbacks; nesting task addition during another task's recording would corrupt graph construction, so it throws.

Source

Thrown at packages/dev/core/src/FrameGraph/frameGraph.ts:182

    /**
     * Gets all tasks of a specific type, based on their class name
     * @param taskClassName Class name(s) of the task(s) to get
     * @returns The list of tasks of the specified type
     */
    public getTasksByClassName<T extends FrameGraphTask>(taskClassName: string | string[]): T[] {
        return Array.isArray(taskClassName)
            ? (this._tasks.filter((t) => taskClassName.includes(t.getClassName())) as T[])
            : (this._tasks.filter((t) => t.getClassName() === taskClassName) as T[]);
    }

    /**
     * Adds a task to the frame graph
     * @param task Task to add
     */
    public addTask(task: FrameGraphTask): void {
        if (this._currentProcessedTask !== null) {
            throw new Error(`FrameGraph.addTask: Can't add the task "${task.name}" while another task is currently building (task: ${this._currentProcessedTask.name}).`);
        }

        if (this._tasks.includes(task)) {
            return;
        }

        this._tasks.push(task);
        this._initAsyncPromises.push(task.initAsync());
    }

    /**
     * Adds a pass to a task. This method can only be called during a Task.record execution.
     * @param name The name of the pass
     * @param whenTaskDisabled If true, the pass will be added to the list of passes to execute when the task is disabled (default is false)
     * @returns The render pass created
     */
    public addPass(name: string, whenTaskDisabled = false): FrameGraphPass<FrameGraphContext> {
        return this._addPass(name, FrameGraphPassType.Normal, whenTaskDisabled) as FrameGraphPass<FrameGraphContext>;

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Move addTask calls outside of record()/build() — add all tasks up front, then call build() once.
  2. Inside a custom task's record(), use frameGraph.addRenderPass/addObjectListPass to create passes instead of adding tasks.
  3. Defer the addTask with a queue that is flushed after build() completes (e.g. in a build().then(...) continuation).

Example fix

// before
public record() {
  this._frameGraph.addTask(new FrameGraphClearTextureTask('clear'));
}
// after
public record() {
  const pass = this._frameGraph.addRenderPass(this.name);
  pass.setExecuteFunc((ctx) => { /* clear work */ });
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (frameGraph['_currentProcessedTask']) {
  throw new Error('Cannot addTask while frame graph is building');
}

Try / catch

try {
  frameGraph.addTask(task);
} catch (e) {
  if (String(e.message).includes('while another task is currently building')) {
    pendingTasks.push(task); // flush after build() resolves
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling frameGraph.addTask() from inside a task's record() execution (e.g. a custom task that tries to add other tasks during recording), or from a callback invoked while build() is processing a task — e.g. CreateScreenshotForFrameGraphAsync triggering recording.

Common situations: Custom FrameGraphTask implementations that add child tasks inside record(); asynchronous code (screenshots, asset loading callbacks) resolving during an active build(); re-entrant build() calls from event handlers.

Related errors


AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30). Data as JSON: /api/errors/39bbd7482153043f. Report an issue: GitHub.