Skip to content

Commit c0ff819

Browse files
committed
fix(wrangler): prevent SQLite users error from being reported to Sentry
1 parent 98422a7 commit c0ff819

File tree

3 files changed

+57
-2
lines changed

3 files changed

+57
-2
lines changed

.changeset/gold-donuts-leave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
fix: prevent reporting SQLite error from `wrangler d1 execute` to Sentry

packages/wrangler/src/__tests__/d1/execute.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import fs from "node:fs";
2-
import { join } from "path";
2+
import { join } from "node:path";
33
import { http, HttpResponse } from "msw";
4+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
5+
import { UserError } from "../../errors";
46
import { mockAccountId, mockApiToken } from "../helpers/mock-account-id";
57
import { mockConsoleMethods } from "../helpers/mock-console";
68
import { useMockIsTTY } from "../helpers/mock-istty";
@@ -185,6 +187,24 @@ describe("execute", () => {
185187
expect(std.out).not.toContain("⛅️ wrangler x.x.x");
186188
});
187189

190+
it("should treat SQLite constraint errors as UserErrors", async () => {
191+
// First create a table with a foreign key constraint
192+
const setupSQL = `
193+
CREATE TABLE users (id INTEGER PRIMARY KEY);
194+
CREATE TABLE posts (id INTEGER PRIMARY KEY, user_id INTEGER, FOREIGN KEY(user_id) REFERENCES users(id));
195+
`;
196+
fs.writeFileSync("setup.sql", setupSQL);
197+
await runWrangler("d1 execute db --file setup.sql --local");
198+
199+
// Now try to violate the foreign key constraint
200+
const violationSQL = `INSERT INTO posts (id, user_id) VALUES (1, 999);`;
201+
fs.writeFileSync("violation.sql", violationSQL);
202+
203+
await expect(
204+
runWrangler("d1 execute db --file violation.sql --local")
205+
).rejects.toThrow(UserError);
206+
});
207+
188208
describe("duration formatting", () => {
189209
mockAccountId({ accountId: "some-account-id" });
190210
mockApiToken();

packages/wrangler/src/d1/execute.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,30 @@ export type QueryResult = {
4141
query?: string;
4242
};
4343

44+
// Common SQLite Codes
45+
// See https://www.sqlite.org/rescode.html#constraint
46+
const SQLITE_RESULT_CODES = [
47+
"SQLITE_ERROR",
48+
"SQLITE_CONSTRAINT",
49+
"SQLITE_MISMATCH",
50+
];
51+
52+
function isSqliteUserError(error: unknown): error is Error {
53+
if (!(error instanceof Error)) {
54+
return false;
55+
}
56+
57+
const message = error.message.toUpperCase();
58+
59+
for (const code of SQLITE_RESULT_CODES) {
60+
if (message.includes(code)) {
61+
return true;
62+
}
63+
}
64+
65+
return false;
66+
}
67+
4468
export function Options(yargs: CommonYargsArgv) {
4569
return options
4670
.Database(yargs)
@@ -305,7 +329,13 @@ async function executeLocally({
305329
try {
306330
results = await db.batch(queries.map((query) => db.prepare(query)));
307331
} catch (e: unknown) {
308-
throw (e as { cause?: unknown })?.cause ?? e;
332+
const cause = (e as { cause?: unknown })?.cause ?? e;
333+
334+
if (isSqliteUserError(cause)) {
335+
throw new UserError(cause.message);
336+
}
337+
338+
throw cause;
309339
} finally {
310340
await mf.dispose();
311341
}

0 commit comments

Comments
 (0)