Skip to content

Self-host: toolkit MCP endpoints 500 on legacy connection rows (empty-string JSON columns, bigint in blob tools_synced_at) — boot migration suggestion #2092

Description

@tejasgadhia

Summary

Self-hosted Executor (Docker, ghcr.io/usefulsoftwareco/executor-selfhost:1.6.10) returns HTTP 500 / JSON-RPC -32603 on POST /mcp/toolkits/<slug> initialize when the connection table contains rows carried over from pre-FumaDB versions. Boot only runs additive schema-ensure (CREATE TABLE IF NOT EXISTS / ALTER TABLE ADD COLUMN) and never migrates existing row payloads, so two legacy value shapes crash the ORM.

Environment

  • Image: ghcr.io/usefulsoftwareco/executor-selfhost:1.6.10
  • DB originally created in the 0.8.x era, carried forward through the FumaDB cutover (May 2026) and unified-provider migration (June 2026)
  • 22 rows in connection

Crash 1: empty string in JSON-mode columns

SQLiteTextJson.mapFromDriverValue does JSON.parse(value). Legacy rows have '' (empty string, not NULL) in JSON-mode columns (credential_write in my case), and JSON.parse('') throws:

StorageError: FumaDB connection.findMany failed
  [cause]: SyntaxError: JSON Parse error: Unexpected EOF
      at mapRelationalRow (serve.js:290690)
      at executor.connections.list (serve.js:284883)
      at mcp.host.create_executor_server

The JSON-mode columns on connection are: item_ids, credential_write, last_health, provider_state. Empty strings in any of them crash every toolkit endpoint because create_executor_server → connections.list runs on every MCP session init.

Scan to find affected rows:

SELECT row_id FROM connection
WHERE credential_write = '' OR last_health = '' OR provider_state = '' OR item_ids = '';

Fix:

UPDATE connection SET credential_write = NULL WHERE credential_write = '';
UPDATE connection SET last_health = NULL WHERE last_health = '';
UPDATE connection SET provider_state = NULL WHERE provider_state = '';
UPDATE connection SET item_ids = NULL WHERE item_ids = '';

Crash 2: integer timestamp in blob column tools_synced_at

tools_synced_at is declared SQLite blob, but legacy rows contain ASCII-digit timestamps (e.g. 1790039911318). The Drizzle bigint decoder receives a JS number and throws:

TypeError: The first argument must be of type string, Buffer, ArrayBuffer, Array,
or Array-like Object. Received type number (1790039911318)
    at mapFromDriverValue (serve.js:291456)

(The existing runSqliteNdjsonOutputMigration already NULLs this column for NDJSON-affected rows — but rows unaffected by that migration keep the crashing values.)

Fix:

UPDATE connection SET tools_synced_at = NULL;  -- forces stale re-sync, safe

Suggestion

A boot-time data migration would save every long-running self-hoster from hand-repairing their DB. Minimal version:

-- Crash 1
UPDATE connection SET credential_write = NULL WHERE credential_write = '';
UPDATE connection SET last_health = NULL WHERE last_health = '';
UPDATE connection SET provider_state = NULL WHERE provider_state = '';
-- Crash 2
UPDATE connection SET tools_synced_at = NULL WHERE typeof(tools_synced_at) = 'blob';

(item_ids is NOT NULL so '' rows there would indicate worse damage; none existed in my DB.)

Alternatively, SQLiteTextJson.mapFromDriverValue could treat '' as NULL instead of throwing, which fixes Crash 1 generically for every JSON column in every table.

Notes

  • This is distinct from Desktop app crashing? Start here #966 (desktop app crash-triage), though the underlying theme is the same: legacy data + new storage layer. The advice there ("reset data") works but throws away connections unnecessarily — the rows above are repairable in place.
  • After applying the two fixes, all toolkit endpoints return 200 on initialize and tools/list with no log errors.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions