{"record":{"id":"bf9e431eb677e13b","repo":"pulumi/pulumi","slug":"scopes-property-did-not-have-value","errorCode":null,"errorMessage":"[[Scopes]] property did not have [value]","messagePattern":"\\[\\[Scopes\\]\\] property did not have \\[value\\]","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"sdk/nodejs/runtime/closure/v8.ts","lineNumber":98,"sourceCode":"    func: Function,\n    freeVariable: string,\n    throwOnFailure: boolean,\n): Promise<any> {\n    // First, find the runtime's internal id for this function.\n    const functionId = await getRuntimeIdForFunctionAsync(func);\n\n    // Now, query for the internal properties the runtime sets up for it.\n    const { internalProperties } = await runtimeGetPropertiesAsync(functionId, /*ownProperties:*/ false);\n\n    // There should normally be an internal property called [[Scopes]]:\n    // https://chromium.googlesource.com/v8/v8.git/+/3f99afc93c9ba1ba5df19f123b93cc3079893c9b/src/inspector/v8-debugger.cc#820\n    const scopes = internalProperties.find((p) => p.name === \"[[Scopes]]\");\n    if (!scopes) {\n        throw new Error(\"Could not find [[Scopes]] property\");\n    }\n\n    if (!scopes.value) {\n        throw new Error(\"[[Scopes]] property did not have [value]\");\n    }\n\n    if (!scopes.value.objectId) {\n        throw new Error(\"[[Scopes]].value have objectId\");\n    }\n\n    // This is sneaky, but we can actually map back from the [[Scopes]] object to a real in-memory\n    // v8 array-like value.  Note: this isn't actually a real array.  For example, it cannot be\n    // iterated.  Nor can any actual methods be called on it. However, we can directly index into\n    // it, and we can.  Similarly, the 'object' type it optionally points at is not a true JS\n    // object.  So we can't call things like .hasOwnProperty on it.  However, the values pointed to\n    // by 'object' are the real in-memory JS objects we are looking for.  So we can find and return\n    // those successfully to our caller.\n    const scopesArray: { object?: Record<string, any> }[] = await getValueForObjectId(scopes.value.objectId);\n\n    // scopesArray is ordered from innermost to outermost.\n    for (let i = 0, n = scopesArray.length; i < n; i++) {\n        const scope = scopesArray[i];","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/pulumi/pulumi/blob/793f7b2e160db4321fb7fb6b0607461e01cb251e/sdk/nodejs/runtime/closure/v8.ts#L80-L116","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nconst fn = handler.bind(context);\nawait serializeFunction(fn);\n\n// after\nconst fn = (...args) => handler.apply(context, args); // plain function with real scopes\nawait serializeFunction(fn);","handlingStrategy":"validation","validationCode":"function assertPlainFunction(fn: unknown) {\n  if (typeof fn !== \"function\") throw new TypeError(\"serializeFunction requires a function\");\n  const s = String(fn);\n  if (s.includes(\"[native code]\")) throw new TypeError(\"Native functions cannot be serialized\");\n}","typeGuard":"function isPlainFunction(v: unknown): v is (...args: unknown[]) => unknown {\n  return typeof v === \"function\" && !String(v).includes(\"[native code]\") && !(v instanceof Proxy);\n}","tryCatchPattern":"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; } }","preventionTips":["Only pass user-authored function literals to serializeFunction.","Run on supported Node LTS versions.","Avoid .bind(), Proxy wrappers, and native functions as serialization targets."],"tags":["nodejs","v8-inspector","closure-serialization"],"backgroundTag":"v8-scopes-property-missing","analyzedSha":"793f7b2e160db4321fb7fb6b0607461e01cb251e","analyzedAt":"2026-08-31T09:36:43.099Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}