Skip to content

Fix migration 0008 parser stack overflow on macOS bun:sqlite#745

Merged
RhysSullivan merged 2 commits intomainfrom
rs/fix-migration-0008-parser-overflow
May 10, 2026
Merged

Fix migration 0008 parser stack overflow on macOS bun:sqlite#745
RhysSullivan merged 2 commits intomainfrom
rs/fix-migration-0008-parser-overflow

Conversation

@RhysSullivan
Copy link
Copy Markdown
Owner

Summary

Migration 0008_scoped_credentials_cutover.sql inlined a 41-deep nested replace() chain to slugify header / scheme / param names into slot_key strings. bun:sqlite's lemon parser stack overflows at PREPARE time on the compiled CLI binary on macOS, so users upgrading on Mac couldn't start the CLI at all — the migration aborts with SQLiteError: parser stack overflow. Linux CI never exercised the path.

Refactor the migration to precompute slugs once into a __slug_norm temp table at the top, via a series of shallow UPDATE statements (one per replacement), then reference the result via flat scalar subqueries (COALESCE((SELECT slug FROM __slug_norm WHERE raw = X), 'default')) at all 34 use sites.

Also add a structural lint test that scans every drizzle migration file and rejects any nested-function call deeper than 20 levels. Catches the regression class on Linux CI without needing macOS in the matrix.

Why

The user-facing failure on macOS:

DrizzleError: Failed to run the query '
INSERT INTO `__openapi_header_slot_preflight` ...
SELECT ... 'header:' || COALESCE(NULLIF(trim(replace(replace(replace(...41 levels...))), ''), 'default') ...
'
Caused by: SQLiteError: parser stack overflow
    at prepare (bun:sqlite:345:37)

Bun ships SQLite compiled with a small YYSTACKDEPTH. The 41-deep chain (8 dash-collapse replace() wrapping lower() wrapping 33 single-char replace() wrapping trim() wrapping the raw input) reaches the limit on the compiled binary's smaller thread stack on macOS. Note that running tests with bunx --bun vitest on Linux also passes — only the compiled binary on macOS trips it.

What changed

  1. apps/local/drizzle/0008_scoped_credentials_cutover.sql — added __slug_norm temp table setup at the top (one UPDATE per character replacement; 41 shallow statements total). Replaced 34 occurrences of the deep chain with COALESCE((SELECT \slug` FROM `__slug_norm` WHERE `raw` = ...), 'default'). DROP TABLE __slug_norm` at the end. Net: +120/-34 lines.
  2. apps/local/src/server/migration-nesting.test.ts — new structural lint test. Walks every migration .sql tracking nested same-name function calls; fails if any single function nests > 20 deep. One test per migration file (11 tests). Verified to fail on the original 0008 and pass on the refactored one.
  3. .changeset/fix-migration-0008-parser-overflow.md"executor": patch so this lands in 1.4.16 before publish (PR 743 is still open; this fix piggybacks on the same Version Packages PR).

Test plan

  • bunx --bun vitest run in apps/local — 51 tests pass (including 40 migration tests across the 0007–0010 path)
  • Slug semantics verified equivalent to the original deep-chain across 25 inputs (case variants, all 33 special chars individually, multi-dash collapse, leading/trailing whitespace, empty string, dashes-only, unicode/emoji)
  • Structural lint catches the original 0008 deep chain; passes on the refactored version
  • Plugin tests pass for openapi (89), mcp (66), graphql (34) — they read the post-migration schema
  • After merge, verify the next pkg-pr-new build of 1.4.16 starts cleanly on a macOS user's ~/.executor/data.db that's at migration 0006 (the failing scenario)

Follow-up (not in this PR)

Add macOS to the test matrix in pkg-pr-new.yml and ci.yml. The structural lint catches THIS shape of bug, but the test matrix gap is real — anything else that's macOS-specific would also slip through.

Migration 0008 inlined a 41-deep nested replace() chain (8 dash-collapse
replaces wrapping lower(33 single-char replaces wrapping the raw input))
to slugify header names into slot_keys. Bun's lemon SQL parser stack
overflows at PREPARE time on the compiled CLI binary on macOS, so users
upgrading on Mac couldn't start the CLI at all (the Linux test matrix
in CI never exercised the failing path).

Replace the deep chain with a temp __slug_norm table populated once at
the top of the migration via a series of shallow UPDATE statements (one
per replacement), then reference the precomputed slug via flat scalar
subqueries everywhere. Semantics verified equivalent against 25 inputs
covering case, punctuation, whitespace, dashes-only and unicode.

Add structural lint that scans every drizzle migration file and rejects
any nested-function call > 20 deep. Catches the regression class on
Linux CI without needing macOS in the matrix.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 10, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
executor-marketing 398028e Commit Preview URL

Branch Preview URL
May 10 2026, 06:33 AM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 10, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
executor-cloud 398028e May 10 2026, 06:33 AM

- Remove .changeset/fix-migration-0008-parser-overflow.md: the existing
  .changeset/executor-1.4.16.md (still in main, consumed when the open
  Version Packages PR merges) is enough to ship 1.4.16 with this fix
  included.
- Replace throw new Error(...) in migration-nesting.test.ts with a
  shaped expect(...).toEqual({...}) that prints file/line/fn/depth in
  the diff. Satisfies oxlint's no-error-constructor / no-try-catch-or-throw
  rules without losing the diagnostic.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 10, 2026

Open in StackBlitz

@executor-js/cli

npm i https://pkg.pr.new/@executor-js/cli@745

@executor-js/config

npm i https://pkg.pr.new/@executor-js/config@745

@executor-js/execution

npm i https://pkg.pr.new/@executor-js/execution@745

@executor-js/sdk

npm i https://pkg.pr.new/@executor-js/sdk@745

@executor-js/storage-core

npm i https://pkg.pr.new/@executor-js/storage-core@745

@executor-js/codemode-core

npm i https://pkg.pr.new/@executor-js/codemode-core@745

@executor-js/runtime-quickjs

npm i https://pkg.pr.new/@executor-js/runtime-quickjs@745

@executor-js/plugin-file-secrets

npm i https://pkg.pr.new/@executor-js/plugin-file-secrets@745

@executor-js/plugin-google-discovery

npm i https://pkg.pr.new/@executor-js/plugin-google-discovery@745

@executor-js/plugin-graphql

npm i https://pkg.pr.new/@executor-js/plugin-graphql@745

@executor-js/plugin-keychain

npm i https://pkg.pr.new/@executor-js/plugin-keychain@745

@executor-js/plugin-mcp

npm i https://pkg.pr.new/@executor-js/plugin-mcp@745

@executor-js/plugin-onepassword

npm i https://pkg.pr.new/@executor-js/plugin-onepassword@745

@executor-js/plugin-openapi

npm i https://pkg.pr.new/@executor-js/plugin-openapi@745

executor

npm i https://pkg.pr.new/executor@745

commit: 398028e

@RhysSullivan RhysSullivan merged commit baf3bd4 into main May 10, 2026
10 checks passed
@RhysSullivan RhysSullivan deleted the rs/fix-migration-0008-parser-overflow branch May 10, 2026 06:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant