pulumi/pulumi · error · Error
function is not a bound function
Error message
function is not a bound function
What it means
During closure serialization the SDK inspects a captured function to determine its runtime/remote execution metadata. When the V8 inspector reports the function has no [[TargetFunction]] internal property, it is not a bound function (created via Function.prototype.bind), so the SDK cannot unwrap the original target and throws. This code path expects `func` to be a bound function whose target can be resolved.
Source
Thrown at sdk/nodejs/runtime/closure/v8.ts:336
}
// Extract value and clear our table entry.
const val = context.calls[tableId];
delete context.calls[tableId];
return val;
}
export async function getBoundFunction(
func: Function,
): Promise<{ targetFunctionText: string; boundThisValue: any; boundArgsValues: any[] }> {
const functionId = await getRuntimeIdForFunctionAsync(func);
const { internalProperties } = await runtimeGetPropertiesAsync(functionId, /*ownProperties:*/ false);
const desc = internalProperties.find((p) => p.name === "[[TargetFunction]]");
const targetFunctionText = desc?.value?.description;
if (!targetFunctionText) {
throw new Error("function is not a bound function");
}
const boundThisValue = internalProperties.find((p) => p.name === "[[BoundThis]]")?.value?.value;
const boundArgsObjectId = internalProperties.find((p) => p.name === "[[BoundArgs]]")?.value?.objectId;
let boundArgsValues: any[] = [];
if (boundArgsObjectId) {
const { properties } = await runtimeGetPropertiesAsync(boundArgsObjectId, /*ownProperties:*/ false);
boundArgsValues = properties.filter((p) => p.enumerable).map((p) => p.value?.value);
}
return { targetFunctionText, boundThisValue, boundArgsValues };
}
View on GitHub (pinned to 793f7b2e16)
Solutions
- Bind the function before serializing: pass `func.bind(null, ...capturedValues)` instead of the raw function.
- Verify the value passed to the serializer is actually a function, not a wrapper object or proxy.
- Check that the Node/V8 version in use still reports [[TargetFunction]] via the inspector protocol; align with a supported runtime version.
Example fix
// before serializeFunction(myHandler); // after serializeFunction(myHandler.bind(null, configValue));
Defensive patterns
Strategy: type-guard
Validate before calling
// before serializing
if (typeof func !== "function") throw new TypeError("expected a function"); Type guard
function isBoundFunction(f: unknown): f is Function {
return typeof f === "function" && f.name.startsWith("bound ");
} Try / catch
try {
await serializeFunction(fn);
} catch (err) {
if ((err as Error).message === "function is not a bound function") {
fn = fn.bind(null, ...captured);
await serializeFunction(fn);
} else throw err;
} Prevention
- Always use fn.bind(null, ...) to capture values for closure serialization
- Assert typeof operand === 'function' before calling the serializer
- Keep the Node runtime on a version supported by the Pulumi SDK
When it happens
Trigger: Calling serializeFunction/captureOutputAsync (directly or via exports in a Pulumi program) with a function whose runtime descriptor is retrieved via getRuntimeIdForFunctionAsync but which is not created with `.bind()` — e.g. passing a plain arrow function or regular function where the serializer expected `fn.bind(null, ...)` with captured args.
Common situations: Custom closure serialization in provider/plugin code; upgrading the Node runtime or enabling V8 flags that change internal property reporting; hand-written code that mimics the SDK's bound-function capture pattern but passes an unbound function.
Related errors
- failed to serialize configuration: %w
- failed to serialize configuration secret keys: %w
- Calling [toJSON] on an [Output<T>] is not supported. To get
- expected '::' in provider reference ${ref}
- Failed to find package.json.
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/8ebe6677ae7c9b19.
Report an issue: GitHub.