{"record":{"id":"caa8fb784b6fb808","repo":"neon-bindings/neon","slug":"attempted-to-settle-jsfuture-multiple-times","errorCode":null,"errorMessage":"Attempted to settle JsFuture multiple times","messagePattern":"Attempted to settle JsFuture multiple times","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/neon/src/types_impl/promise.rs","lineNumber":201,"sourceCode":"        let (tx, rx) = oneshot::channel();\n        let take_state = {\n            // Note: If this becomes a bottleneck, `unsafe` could be used to avoid it.\n            // The promise spec guarantees that it will only be used once.\n            let state = Arc::new(Mutex::new(Some((f, tx))));\n\n            move || {\n                state\n                    .lock()\n                    .ok()\n                    .and_then(|mut lock| lock.take())\n                    // This should never happen because `self` is a native `Promise`\n                    // and settling multiple times is a violation of the spec.\n                    .expect(\"Attempted to settle JsFuture multiple times\")\n            }\n        };\n\n        let resolve = JsFunction::new(cx, {\n            let take_state = take_state.clone();\n\n            move |mut cx| {\n                let (f, tx) = take_state();\n                let v = cx.argument::<JsValue>(0)?;","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/neon-bindings/neon/blob/38960e4381d9ad13b551cdf2d261f609167c9bc2/crates/neon/src/types_impl/promise.rs#L183-L219","documentation":"`JsFuture::to_future` installs `then`/`catch` callbacks that each `take()` a shared `Mutex<Option<...>>` to run the continuation exactly once. The `.expect` fires when the state slot is already empty — i.e. the promise's then/catch callbacks were both invoked, which the JavaScript promise spec forbids. This is a defensive invariant check against a spec-violating or foreign promise-like object.","triggerScenarios":"Inside the generated resolve/reject callbacks, `take_state()` panics if `lock.take()` returns None — the callback is invoked a second time (or both resolve and reject fire), which only happens if `self` is not a spec-compliant native Promise.","commonSituations":"Passing a non-promise 'thenable' with broken semantics (calls then twice) into to_future; misbehaved mocks or polyfills; embedding engines whose promise implementation violates the spec.","solutions":["Ensure the object passed to to_future is a genuine native Promise, not a hand-rolled thenable","Fix or drop custom thenable/polyfill implementations that settle twice","Validate inputs with `instanceof Promise` before calling to_future in generic APIs"],"exampleFix":"Only call `to_future` on real native Promise objects (`cx.promise()` results or values verified as Promise instances); replace or fix any thenable that invokes its callbacks more than once.","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Document that to_future requires spec-compliant promises","Add input validation (type/instance checks) at API boundaries accepting promises"],"tags":[],"backgroundTag":null,"analyzedSha":"38960e4381d9ad13b551cdf2d261f609167c9bc2","analyzedAt":"2026-09-13T09:05:33.640Z","contentChangedAt":"2026-09-13T09:05:33.640Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}