BabylonJS/Babylon.js · error

FrameGraph: A pass must be created during a Task.record exec

Error message

FrameGraph: A pass must be created during a Task.record execution only.

What it means

FrameGraph._addPass() throws when a pass (render pass or object list pass) is created while no task is currently being recorded. Passes are owned by tasks; creating one outside Task.record() execution would leave it orphaned in the graph, so the frame graph forbids it.

Source

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

     * @returns The render pass created
     */
    public addRenderPass(name: string, whenTaskDisabled = false): FrameGraphRenderPass {
        return this._addPass(name, FrameGraphPassType.Render, whenTaskDisabled) as FrameGraphRenderPass;
    }

    /**
     * Adds an object list 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 object list pass created
     */
    public addObjectListPass(name: string, whenTaskDisabled = false): FrameGraphObjectListPass {
        return this._addPass(name, FrameGraphPassType.ObjectList, whenTaskDisabled) as FrameGraphObjectListPass;
    }

    private _addPass(name: string, passType: FrameGraphPassType, whenTaskDisabled = false): FrameGraphPass<FrameGraphContext> | FrameGraphRenderPass {
        if (!this._currentProcessedTask) {
            throw new Error("FrameGraph: A pass must be created during a Task.record execution only.");
        }

        let pass: FrameGraphPass<FrameGraphContext> | FrameGraphRenderPass;

        switch (passType) {
            case FrameGraphPassType.Render:
                pass = new FrameGraphRenderPass(name, this._currentProcessedTask, this._renderContext, this._engine);
                break;
            case FrameGraphPassType.ObjectList:
                pass = new FrameGraphObjectListPass(name, this._currentProcessedTask, this._passContext, this._engine);
                break;
            default:
                pass = new FrameGraphPass(name, this._currentProcessedTask, this._passContext);
                break;
        }

        this._currentProcessedTask._addPass(pass, whenTaskDisabled);

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Create passes only inside your custom task's record() method, invoked via frameGraph.build().
  2. Do not call task.record() directly; add the task with frameGraph.addTask(task) and build the graph.
  3. If you need pass-like work outside a task, wrap it in a small custom FrameGraphTask and add it to the graph.

Example fix

// before
const pass = frameGraph.addRenderPass('myPass'); // outside any task
// after
class MyTask extends FrameGraphTask {
  public record() {
    const pass = this._frameGraph.addRenderPass(this.name);
    pass.setExecuteFunc((ctx) => { /* ... */ });
  }
}
frameGraph.addTask(new MyTask('myTask'));
frameGraph.build();
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const pass = frameGraph.addRenderPass('x');
} catch (e) {
  if (String(e.message).includes('Task.record execution only')) {
    // move pass creation into a FrameGraphTask.record()
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling frameGraph.addRenderPass()/addObjectListPass()/addPass() from application code outside any task's record(), or from a custom task whose record() is not being executed by frameGraph.build() (e.g. calling task.record() directly without going through the frame graph build loop).

Common situations: Manually invoking record() on a custom task instead of using frameGraph.build(); creating render passes at setup time in initialization code; a task holding a stale frameGraph reference whose build was already completed.

Related errors


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