DT
Drizzle Team•2mo ago
seth

Stack trace on constraint failed doesn't include my application code

I'm just getting started using drizzle on a new app, with sqlite and nuxt 3. Everything has been great, but there's one thing happening that really concerns me: when I generate an insert that fails a NOT NULL constraint, I get an error, but the stack trace doesn't include my application code. This is worrying me, because how would I find the issue if my app was large? Is it normal for drizzle stack traces to not include the calling function in the stack trace? If not, any suggestions how I go about fixing this? Example: because my code is small, I know this error was generated by server/utils/drizzle.ts at line 80 in the function selectOrCreateSession:
ERROR undefined Application error: NOT NULL constraint failed: universal_sessions.userID

at Object.next (node_modules/libsql/index.js:335:20)
at Statement.all (node_modules/libsql/index.js:360:16)
at executeStmt (node_modules/@libsql/client/lib-esm/sqlite3.js:261:34)
at Sqlite3Client.execute (node_modules/@libsql/client/lib-esm/sqlite3.js:79:16)
at LibSQLPreparedQuery.values (node_modules/src/libsql/session.ts:262:57)
at LibSQLPreparedQuery.all (node_modules/src/libsql/session.ts:191:27)
at QueryPromise.all (node_modules/src/sqlite-core/query-builders/insert.ts:396:26)
at QueryPromise.execute (node_modules/src/sqlite-core/query-builders/insert.ts:408:40)
at QueryPromise.then (node_modules/src/query-promise.ts:31:15)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)

// END OF STACK TRACE :-( Where is `at selectOrCreateSession (server/utils/drizzle.ts)` ???
ERROR undefined Application error: NOT NULL constraint failed: universal_sessions.userID

at Object.next (node_modules/libsql/index.js:335:20)
at Statement.all (node_modules/libsql/index.js:360:16)
at executeStmt (node_modules/@libsql/client/lib-esm/sqlite3.js:261:34)
at Sqlite3Client.execute (node_modules/@libsql/client/lib-esm/sqlite3.js:79:16)
at LibSQLPreparedQuery.values (node_modules/src/libsql/session.ts:262:57)
at LibSQLPreparedQuery.all (node_modules/src/libsql/session.ts:191:27)
at QueryPromise.all (node_modules/src/sqlite-core/query-builders/insert.ts:396:26)
at QueryPromise.execute (node_modules/src/sqlite-core/query-builders/insert.ts:408:40)
at QueryPromise.then (node_modules/src/query-promise.ts:31:15)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)

// END OF STACK TRACE :-( Where is `at selectOrCreateSession (server/utils/drizzle.ts)` ???
Is this expected? How would I debug this if I end up with a LARGE drizzle app? I've tried starting nuxt with: NODE_OPTIONS='--trace-uncaught' nuxt dev, because yeah, general node issue, but no luck.
5 Replies
RWOverdijk
RWOverdijk•2mo ago
I don't have the answer, so I will be following along. But I am curious, are you using prepared statements by any chance?
seth
sethOP•2mo ago
No prepared statements, although using them is one of my next ideas. Do you ever see this issue, or is this particular to me?
RWOverdijk
RWOverdijk•2mo ago
I haven't had this before, no. But usually this happens because it's being cut off by how the async chain is being handled. So maybe you have a return statement somewhere before awaiting a promise? So you have a chain that finishes before your context is done. If not, maybe drizzle does. If also not that, some other reason the context is severed. Otherwise I am out of ideas sorry 😅
seth
sethOP•2mo ago
Yeah, I suspected the async chain too, I always get a little confused at this point lol. That's why I hoped --trace-uncaught would change things, but no luck. I'm seeing this in another context too, I'm wondering if something nuxt is doing is making this more likely to happen. something nuxt is doing or something I'm doing wrong with nuxt I should say lol Here's a "minimal repro" example with nuxt 3, maybe this will inspire somebody:
// server/api/session/[id].get.ts

import { drizzle } from 'drizzle-orm/libsql'
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"

const schema = {
universalSessions: sqliteTable("universal_sessions", {
id: text().primaryKey(),
userID: text().notNull(),
createdAt: integer({ mode: "timestamp" }).notNull(),
})
}

export default defineEventHandler(async (event) => {
const universalSessionID = event.context.params?.id
if (!universalSessionID) throw Error("Missing universalSessionID")

const db = drizzle('file:universal.sqlite', {schema})

const universalSession = await db.query.universalSessions.findFirst({
where: (universalSession, { eq }) => eq(universalSession.id, universalSessionID),
with: {
withThingThatDoesntExistToMakeError: true
}
})

console.log("Got universalSession", universalSession)

return universalSession
})
// server/api/session/[id].get.ts

import { drizzle } from 'drizzle-orm/libsql'
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"

const schema = {
universalSessions: sqliteTable("universal_sessions", {
id: text().primaryKey(),
userID: text().notNull(),
createdAt: integer({ mode: "timestamp" }).notNull(),
})
}

export default defineEventHandler(async (event) => {
const universalSessionID = event.context.params?.id
if (!universalSessionID) throw Error("Missing universalSessionID")

const db = drizzle('file:universal.sqlite', {schema})

const universalSession = await db.query.universalSessions.findFirst({
where: (universalSession, { eq }) => eq(universalSession.id, universalSessionID),
with: {
withThingThatDoesntExistToMakeError: true
}
})

console.log("Got universalSession", universalSession)

return universalSession
})
If I comment out the with(), everything works, its a valid query. The with just makes the query invalid, and it results in a stack trace without the application code in the stack like:
ERROR /api/session/01JPCGWVA2M0C2WCM62QZB0V4Y Application error: Cannot read properties of undefined (reading 'referencedTable')

at normalizeRelation (node_modules/src/relations.ts:571:74)
at SQLiteAsyncDialect.buildRelationalQuery (node_modules/src/sqlite-core/dialect.ts:660:32)
at QueryPromise._toSQL (node_modules/src/sqlite-core/query-builders/query.ts:169:30)
at QueryPromise._prepare (node_modules/src/sqlite-core/query-builders/query.ts:145:38)
at QueryPromise.executeRaw (node_modules/src/sqlite-core/query-builders/query.ts:191:16)
at QueryPromise.execute (node_modules/src/sqlite-core/query-builders/query.ts:197:15)
at QueryPromise.then (node_modules/src/query-promise.ts:31:15)
ERROR /api/session/01JPCGWVA2M0C2WCM62QZB0V4Y Application error: Cannot read properties of undefined (reading 'referencedTable')

at normalizeRelation (node_modules/src/relations.ts:571:74)
at SQLiteAsyncDialect.buildRelationalQuery (node_modules/src/sqlite-core/dialect.ts:660:32)
at QueryPromise._toSQL (node_modules/src/sqlite-core/query-builders/query.ts:169:30)
at QueryPromise._prepare (node_modules/src/sqlite-core/query-builders/query.ts:145:38)
at QueryPromise.executeRaw (node_modules/src/sqlite-core/query-builders/query.ts:191:16)
at QueryPromise.execute (node_modules/src/sqlite-core/query-builders/query.ts:197:15)
at QueryPromise.then (node_modules/src/query-promise.ts:31:15)
In contrast, throwing an async error inside a nuxt 3 event handler doesn't normally result in a "disconnected" stack trace, for example:
// server/api/session/[id].get.ts

async function throwAsyncError() {
await new Promise(resolve => setTimeout(resolve, 1000))
throw Error('fail in a boring way')
}

export default defineEventHandler(async (event) => {
await throwAsyncError()
})
// server/api/session/[id].get.ts

async function throwAsyncError() {
await new Promise(resolve => setTimeout(resolve, 1000))
throw Error('fail in a boring way')
}

export default defineEventHandler(async (event) => {
await throwAsyncError()
})
Results in the connected stack trace (note the first line references server/api/session[id].get.ts):
ERROR /api/session/01JPCGWVA2M0C2WCM62QZB0V4Y Application error: fail in a boring way

at throwAsyncError (server/api/session/[id].get.ts:40:1)
at async Object.handler (server/api/universal-session/[id].get.ts:44:1)
at async node_modules/h3/dist/index.mjs:2009:19
at async Object.callAsync (node_modules/unctx/dist/index.mjs:72:16)
at async Server.toNodeHandle (node_modules/h3/dist/index.mjs:2301:7)
ERROR /api/session/01JPCGWVA2M0C2WCM62QZB0V4Y Application error: fail in a boring way

at throwAsyncError (server/api/session/[id].get.ts:40:1)
at async Object.handler (server/api/universal-session/[id].get.ts:44:1)
at async node_modules/h3/dist/index.mjs:2009:19
at async Object.callAsync (node_modules/unctx/dist/index.mjs:72:16)
at async Server.toNodeHandle (node_modules/h3/dist/index.mjs:2301:7)
RWOverdijk
RWOverdijk•2mo ago
I don’t know what’s going on with “defineEventHandler”. I’m on my phone so it’s hard to read. But event handlers, especially async ones, are easily detached. Could that be the issue?

Did you find this page helpful?