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

  1. Verify the object passed to serializeFunction is an ordinary JavaScript function declared in JS/TS (not a native, bound, or proxy-wrapped function).
  2. Upgrade to a current Node.js LTS version supported by the SDK, since inspector internal-property shapes vary across V8 versions.
  3. Avoid running inside runtimes that restrict the inspector protocol (some serverless/embedded environments); serialize in the normal CLI host process.
  4. 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

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


AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31). Data as JSON: /api/errors/bf9e431eb677e13b. Report an issue: GitHub.