diff --git a/.changeset/gold-donuts-leave.md b/.changeset/gold-donuts-leave.md new file mode 100644 index 000000000000..f81cdc2be978 --- /dev/null +++ b/.changeset/gold-donuts-leave.md @@ -0,0 +1,5 @@ +--- +"wrangler": patch +--- + +fix: prevent reporting SQLite error from `wrangler d1 execute` to Sentry diff --git a/packages/wrangler/src/__tests__/d1/execute.test.ts b/packages/wrangler/src/__tests__/d1/execute.test.ts index f8a69ae087bf..e019e9192209 100644 --- a/packages/wrangler/src/__tests__/d1/execute.test.ts +++ b/packages/wrangler/src/__tests__/d1/execute.test.ts @@ -1,6 +1,8 @@ import fs from "node:fs"; -import { join } from "path"; +import { join } from "node:path"; import { http, HttpResponse } from "msw"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { UserError } from "../../errors"; import { mockAccountId, mockApiToken } from "../helpers/mock-account-id"; import { mockConsoleMethods } from "../helpers/mock-console"; import { useMockIsTTY } from "../helpers/mock-istty"; @@ -185,6 +187,24 @@ describe("execute", () => { expect(std.out).not.toContain("⛅️ wrangler x.x.x"); }); + it("should treat SQLite constraint errors as UserErrors", async () => { + // First create a table with a foreign key constraint + const setupSQL = ` + CREATE TABLE users (id INTEGER PRIMARY KEY); + CREATE TABLE posts (id INTEGER PRIMARY KEY, user_id INTEGER, FOREIGN KEY(user_id) REFERENCES users(id)); + `; + fs.writeFileSync("setup.sql", setupSQL); + await runWrangler("d1 execute db --file setup.sql --local"); + + // Now try to violate the foreign key constraint + const violationSQL = `INSERT INTO posts (id, user_id) VALUES (1, 999);`; + fs.writeFileSync("violation.sql", violationSQL); + + await expect( + runWrangler("d1 execute db --file violation.sql --local") + ).rejects.toThrow(UserError); + }); + describe("duration formatting", () => { mockAccountId({ accountId: "some-account-id" }); mockApiToken(); diff --git a/packages/wrangler/src/d1/execute.ts b/packages/wrangler/src/d1/execute.ts index 169125315584..ffadcb44f119 100644 --- a/packages/wrangler/src/d1/execute.ts +++ b/packages/wrangler/src/d1/execute.ts @@ -41,6 +41,30 @@ export type QueryResult = { query?: string; }; +// Common SQLite Codes +// See https://www.sqlite.org/rescode.html#constraint +const SQLITE_RESULT_CODES = [ + "SQLITE_ERROR", + "SQLITE_CONSTRAINT", + "SQLITE_MISMATCH", +]; + +function isSqliteUserError(error: unknown): error is Error { + if (!(error instanceof Error)) { + return false; + } + + const message = error.message.toUpperCase(); + + for (const code of SQLITE_RESULT_CODES) { + if (message.includes(code)) { + return true; + } + } + + return false; +} + export function Options(yargs: CommonYargsArgv) { return options .Database(yargs) @@ -305,7 +329,13 @@ async function executeLocally({ try { results = await db.batch(queries.map((query) => db.prepare(query))); } catch (e: unknown) { - throw (e as { cause?: unknown })?.cause ?? e; + const cause = (e as { cause?: unknown })?.cause ?? e; + + if (isSqliteUserError(cause)) { + throw new UserError(cause.message); + } + + throw cause; } finally { await mf.dispose(); }