diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 1bea9f52c3ec..bacaa4113b80 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -77,12 +77,20 @@ export const cursor = { }, } -const info = (row: typeof MessageTable.$inferSelect) => - ({ - ...row.data, +const info = (row: typeof MessageTable.$inferSelect) => { + const data = row.data as Record + // JSON.stringify(NaN) → null in SQLite; on read-back null violates Schema.optional(Schema.Finite). + // Sanitize affected numeric fields so existing corrupted rows don't cause 500s. + if (data.tokens) { + if (data.tokens.total === null) data.tokens.total = undefined + } + if (data.cost === null) data.cost = 0 + return { + ...data, id: row.id, sessionID: row.session_id, - }) as Info + } as Info +} const part = (row: typeof PartTable.$inferSelect) => ({ diff --git a/packages/opencode/src/session/session.ts b/packages/opencode/src/session/session.ts index de8c3dc4cbd1..48ee159a5b84 100644 --- a/packages/opencode/src/session/session.ts +++ b/packages/opencode/src/session/session.ts @@ -366,9 +366,10 @@ export const getUsage = (input: { model: Provider.Model; usage: Usage; metadata? const adjustedInputTokens = safe(inputTokens - cacheReadInputTokens - cacheWriteInputTokens) const total = input.usage.totalTokens + const safeTotal = total !== undefined && Number.isFinite(total) ? Math.max(0, total) : undefined const tokens = { - total, + total: safeTotal, input: adjustedInputTokens, output: safe(outputTokens - reasoningTokens), reasoning: reasoningTokens,