{"record":{"id":"78455ddd8e4ab5d9","repo":"pulumi/pulumi","slug":"scopes-value-have-objectid","errorCode":null,"errorMessage":"[[Scopes]].value have objectId","messagePattern":"\\[\\[Scopes\\]\\]\\.value have objectId","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"sdk/nodejs/runtime/closure/v8.ts","lineNumber":102,"sourceCode":"    // 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];\n        if (scope.object) {\n            if (freeVariable in scope.object) {\n                const val = scope.object[freeVariable];\n                return val;","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/pulumi/pulumi/blob/793f7b2e160db4321fb7fb6b0607461e01cb251e/sdk/nodejs/runtime/closure/v8.ts#L84-L120","documentation":"After finding the [[Scopes]] internal property, the serializer requires its value to be a remote object handle with an objectId so it can query the scope chain via the inspector protocol. This error fires when the value exists but carries no objectId, so the scope chain cannot be dereferenced.","triggerScenarios":"Runtime.getProperties (v8.ts:102) returns [[Scopes]].value without an objectId — i.e. V8 returned the scopes as a non-object remote value (e.g. type \"undefined\"/\"primitive\") instead of a heap object handle, usually when the inspected target lacks a real lexical environment.","commonSituations":"Serializing built-in/native functions or class constructors without scope objects; inspector responses under mismatched Node/SDK versions; passing a non-function that the protocol still reports as having internal properties.","solutions":["Pass a plain user-defined function (function declaration/expression or arrow) to serializeFunction — not native, bound, or constructor functions.","Align Node.js version with the SDK's supported engines; inspector handle semantics changed across V8 releases.","Check that no runtime proxying/wrapping library replaces your handler with a Proxy or exotic callable before serialization.","If the issue persists, capture and report the remoteObject JSON from the error to narrow down the V8 behavior."],"exampleFix":"// before\nawait serializeFunction(Array.prototype.map); // native fn, no real scopes\n\n// after\nconst fn = () => { /* user code */ };\nawait serializeFunction(fn);","handlingStrategy":"validation","validationCode":"function assertInspectableFunction(fn: unknown) {\n  if (typeof fn !== \"function\") throw new TypeError(\"Expected a function\");\n  if (Object.getPrototypeOf(fn) === Function.prototype && fn.toString().includes(\"[native code]\")) {\n    throw new TypeError(\"Native/built-in functions have no inspectable scope chain\");\n  }\n}","typeGuard":"const isJsFunction = (v: unknown): v is Function =>\n  typeof v === \"function\" && !String(v).includes(\"[native code]\");","tryCatchPattern":"try { await serializeFunction(fn); } catch (e) { if (/\\[\\[Scopes\\]\\]/.test(String(e))) { throw new Error(\"Function has no inspectable scopes; use a plain JS function.\", { cause: e }); } throw e; }","preventionTips":["Serialize only ordinary JS/TS function expressions or declarations.","Pin Node to versions listed in the SDK's engines field.","Strip exotic wrappers (bound, proxied, native) before serializing."],"tags":["nodejs","v8-inspector","closure-serialization"],"backgroundTag":"v8-scopes-property-missing","analyzedSha":"793f7b2e160db4321fb7fb6b0607461e01cb251e","analyzedAt":"2026-08-31T09:36:43.099Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}