Skip to content

[Bug] save_failed: this.withWriteTransaction undefined — appendSceneEvent loses this binding in SceneOperationsFacade #706

Description

@seasonsbrick-cell

What happened?

Calling create_from_template with save: true throws:

save_failed: undefined is not an object (evaluating 'this.withWriteTransaction')

Root cause: In SceneOperationsFacade.appendSceneEvent() (dist/operations/scene-operations.js), the store method is destructured from its object and then called as a plain function — losing the this binding:

// BUGGY (before fix)
async appendSceneEvent(options) {
    const append = this.requireStore().appendSceneEvent; // method extracted
    if (!append) return null;
    return append(options); // called without this → this.withWriteTransaction is undefined
}

When append(options) is called without a receiver, this inside SqliteSceneStore.appendSceneEvent is undefined (strict mode), and the first line return this.withWriteTransaction(...) throws.

The same pattern in listSceneEvents has the identical bug (store method destructured and called without binding).

Call chain:
create-from-template.js:126appendLiveSceneEvent(bridge, ...)
live-sync.js:61 operations.appendSceneEvent({...})
scene-operations.js:169 const append = this.requireStore().appendSceneEvent ← loses this
scene-operations.js:172 append(options)
sqlite-scene-store.js:385 this.withWriteTransaction(...)this is undefined → crash

Steps to reproduce

  1. Install @pascal-app/mcp v0.3.2 globally: bun install -g @pascal-app/mcp
  2. Set PASCAL_DATA_DIR to a writable directory
  3. Call the create_from_template MCP tool with { "id": "two-bedroom", "save": true }
  4. Observe save_failed: undefined is not an object (evaluating 'this.withWriteTransaction')

Minimal reproduction (Node/Bun script):

import { SqliteSceneStore } from '@pascal-app/mcp/dist/storage/sqlite-scene-store.js';
import { SceneBridge } from '@pascal-app/mcp/dist/bridge/scene-bridge.js';
import { createSceneOperations } from '@pascal-app/mcp/dist/operations/scene-operations.js';

const store = new SqliteSceneStore({ dbPath: ':memory:' });
const bridge = new SceneBridge();
const operations = createSceneOperations({ bridge, store });

const meta = await operations.saveScene({ name: 'test', graph: { nodes: {}, rootNodeIds: [] }, saveMode: 'draft', publish: false, operation: 'test' });

// This line throws:
await operations.appendSceneEvent({ sceneId: meta.id, version: meta.version, kind: 'test', graph: { nodes: {}, rootNodeIds: [] } });
// Error: undefined is not an object (evaluating 'this.withWriteTransaction')

Expected behavior

create_from_template with save: true should save the scene and return SceneMeta without error. The appendSceneEvent call that follows the save should succeed, appending a live event to the SQLite store.

Browser & OS

N/A — MCP stdio server, Bun 1.3.13 / macOS 25.5.0 (Darwin). Not a browser issue.

Screenshots or screen recordings

No response

Additional context

Package version: @pascal-app/mcp 0.3.2
Runtime: Bun 1.3.13
Affected methods: SceneOperationsFacade.appendSceneEvent and SceneOperationsFacade.listSceneEvents in dist/operations/scene-operations.js

One-line fix (two places in scene-operations.js):

// appendSceneEvent
-    async appendSceneEvent(options) {
-        const append = this.requireStore().appendSceneEvent;
-        if (!append)
-            return null;
-        return append(options);
-    }
+    async appendSceneEvent(options) {
+        const store = this.requireStore();
+        if (!store.appendSceneEvent)
+            return null;
+        return store.appendSceneEvent(options);
+    }

// listSceneEvents
-    async listSceneEvents(id, options) {
-        const list = this.requireStore().listSceneEvents;
-        if (!list) {
-            throw new Error('scene_events_unavailable');
-        }
-        return list(id, options);
-    }
+    async listSceneEvents(id, options) {
+        const store = this.requireStore();
+        if (!store.listSceneEvents) {
+            throw new Error('scene_events_unavailable');
+        }
+        return store.listSceneEvents(id, options);
+    }

Verified locally: after applying the patch, saveScene + appendSceneEvent + listSceneEvents all succeed against an in-memory SQLite store. Related to #696 (same package, different method).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions