pulumi/pulumi · error · Error
[[Scopes]] property did not have [value]
Error message
[[Scopes]] property did not have [value]
What it means
During closure serialization the SDK uses the Node inspector (V8 debugger protocol) to inspect the function's internal [[Scopes]] property, which holds its lexical environment. It throws this error when the debugger returns a [[Scopes]] internal property descriptor whose value is missing/undefined, meaning V8's response did not contain the scope chain object the serializer needs.
Source
Thrown at sdk/nodejs/runtime/closure/v8.ts:98
func: Function,
freeVariable: string,
throwOnFailure: boolean,
): Promise<any> {
// First, find the runtime's internal id for this function.
const functionId = await getRuntimeIdForFunctionAsync(func);
// Now, query for the internal properties the runtime sets up for it.
const { internalProperties } = await runtimeGetPropertiesAsync(functionId, /*ownProperties:*/ false);
// There should normally be an internal property called [[Scopes]]:
// https://chromium.googlesource.com/v8/v8.git/+/3f99afc93c9ba1ba5df19f123b93cc3079893c9b/src/inspector/v8-debugger.cc#820
const scopes = internalProperties.find((p) => p.name === "[[Scopes]]");
if (!scopes) {
throw new Error("Could not find [[Scopes]] property");
}
if (!scopes.value) {
throw new Error("[[Scopes]] property did not have [value]");
}
if (!scopes.value.objectId) {
throw new Error("[[Scopes]].value have objectId");
}
// This is sneaky, but we can actually map back from the [[Scopes]] object to a real in-memory
// v8 array-like value. Note: this isn't actually a real array. For example, it cannot be
// iterated. Nor can any actual methods be called on it. However, we can directly index into
// it, and we can. Similarly, the 'object' type it optionally points at is not a true JS
// object. So we can't call things like .hasOwnProperty on it. However, the values pointed to
// by 'object' are the real in-memory JS objects we are looking for. So we can find and return
// those successfully to our caller.
const scopesArray: { object?: Record<string, any> }[] = await getValueForObjectId(scopes.value.objectId);
// scopesArray is ordered from innermost to outermost.
for (let i = 0, n = scopesArray.length; i < n; i++) {
const scope = scopesArray[i];View on GitHub (pinned to 793f7b2e16)
Solutions
- Verify the object passed to serializeFunction is an ordinary JavaScript function declared in JS/TS (not a native, bound, or proxy-wrapped function).
- Upgrade to a current Node.js LTS version supported by the SDK, since inspector internal-property shapes vary across V8 versions.
- Avoid running inside runtimes that restrict the inspector protocol (some serverless/embedded environments); serialize in the normal CLI host process.
- Refactor the callback to a plain arrow/function literal with explicit parameters so its scope chain is fully inspectable.
Example fix
// before const fn = handler.bind(context); await serializeFunction(fn); // after const fn = (...args) => handler.apply(context, args); // plain function with real scopes await serializeFunction(fn);
Defensive patterns
Strategy: validation
Validate before calling
function assertPlainFunction(fn: unknown) {
if (typeof fn !== "function") throw new TypeError("serializeFunction requires a function");
const s = String(fn);
if (s.includes("[native code]")) throw new TypeError("Native functions cannot be serialized");
} Type guard
function isPlainFunction(v: unknown): v is (...args: unknown[]) => unknown {
return typeof v === "function" && !String(v).includes("[native code]") && !(v instanceof Proxy);
} Try / catch
try { await serializeFunction(fn); } catch (e) { if (String(e).includes("[[Scopes]]")) { console.error("Target is not a serializable JS function; rewrite as a plain function literal.", e); } else { throw e; } } Prevention
- Only pass user-authored function literals to serializeFunction.
- Run on supported Node LTS versions.
- Avoid .bind(), Proxy wrappers, and native functions as serialization targets.
When it happens
Trigger: Runtime.getProperties (v8.ts:98) returns an internalProperties entry named "[[Scopes]]" but with no value field — typically when the inspector protocol session returned a truncated/degenerate response, or the inspected value is not a real JS function (e.g. a proxy, bound native, or object masquerading as a function).
Common situations: Serializing exotic function-like objects (native/bound functions without lexical scopes); running under environments where the inspector protocol is degraded (restricted V8, Electron/utility processes, patched runtimes); Node version incompatibilities in the inspector API.
Related errors
- [[Scopes]].value have objectId
- Unexpected missing variable in closure environment: ${freeVa
- Error calling "Runtime.evaluate(${expression})" on context $
- Remote object was not 'function': ${JSON.stringify(remoteObj
- Remote function does not have 'objectId': ${JSON.stringify(r
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/bf9e431eb677e13b.
Report an issue: GitHub.