{"record":{"id":"05f861fcc57cc2b2","repo":"perspective-dev/perspective","slug":"unknown-binding-mode-binding-mode","errorCode":null,"errorMessage":"unknown binding mode: ${binding_mode}","messagePattern":"unknown binding mode: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/anywidget/src/js/index.js","lineNumber":141,"sourceCode":"    };\n    model.on(\"msg:custom\", on_custom_msg);\n    model.send({ type: \"connect\", client_id }, null);\n\n    const binding_mode = model.get(\"binding_mode\");\n    const table_name = model.get(\"table_name\");\n    if (!table_name) {\n        throw new Error(\"table_name not set in model\");\n    }\n\n    const table_promise = psp_client.open_table(table_name).then(async (t) => {\n        if (binding_mode === \"client-server\") {\n            const local_client = await perspective.worker();\n            const remote_view = await t.view();\n            return await local_client.table(remote_view);\n        } else if (binding_mode === \"server\") {\n            return t;\n        } else {\n            throw new Error(`unknown binding mode: ${binding_mode}`);\n        }\n    });\n\n    // The viewer's `load()` also accepts a `Client` (with `table` naming the\n    // binding in `ViewerConfig`) since the workspace->viewer merge, but the\n    // single-table widget loads a `Table` directly and treats `table_name` as\n    // the source of truth. The config's derived `table` field therefore has no\n    // widget trait and is excluded from the config sync below. Revisit if the\n    // widget ever grows multi-table (master/detail, global filter) support.\n    await viewer.load(table_promise);\n    await viewer.restore(\n        Object.fromEntries(PERSISTENT_ATTRIBUTES.map((k) => [k, model.get(k)])),\n    );\n\n    // Bidirectional config sync as a single serialized reconciler. The viewer\n    // (`perspective-config-update`) and the model (`change:`) each request a\n    // reconcile in a fixed direction, and tasks run one at a time on\n    // `reconcile` so a `restore()` never overlaps a `save()` read (no stale","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/perspective-dev/perspective/blob/11c8238c0c9c0b9e490b5a6a2dc277afad1e980a/packages/anywidget/src/js/index.js#L123-L159","documentation":"render() branches on the widget's `binding_mode` trait to decide between client-server and server-bound table wiring; when the trait holds any value other than the supported \"client-server\" (e.g. a typo or an outdated kernel/frontend version mismatch), no code path exists and the render aborts.","triggerScenarios":"Thrown at packages/anywidget/src/js/index.js:141 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Set binding_mode to \"client-server\" (or the supported server mode) on the Python widget","Check that the Python perspective package and the JS widget versions match, so both sides agree on supported binding modes","Inspect the received value (console.log(binding_mode)) to spot typos like \"client_server\""],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"11c8238c0c9c0b9e490b5a6a2dc277afad1e980a","analyzedAt":"2026-09-09T00:35:19.377Z","contentChangedAt":"2026-09-09T00:35:19.377Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}