-
Notifications
You must be signed in to change notification settings - Fork 2.1k
[Regression 7.4.1] Nested Uint8Array in Json fields serialized as numeric-keyed objects instead of base64 strings #29267
Description
Bug description
Uint8Array values nested inside objects or arrays are no longer serialized to base64 strings when stored in a Json field. Instead, they are expanded into plain objects with numeric keys (e.g. {"0":72,"1":101,"2":108,"3":108,"4":111}), which is the default JSON.stringify behavior for Uint8Array.
This is a regression introduced in 7.4.1. In 7.3.0, nested Uint8Array values were correctly serialized to base64 strings (e.g. "SGVsbG8=").
Note: Top-level Uint8Array passed directly to a Json field is still correctly serialized to base64 in both versions. The bug only affects Uint8Array values that are nested inside objects or arrays.
Severity
🚨 Critical: Data loss, app crash, security issue
Reproduction
Minimal schema
generator client {
provider = "prisma-client"
output = "../generated/prisma"
}
datasource db {
provider = "sqlite"
}
model TestRecord {
id Int @id @default(autoincrement())
data Json
}Reproduction script
import { PrismaClient } from "./generated/prisma/client.js";
import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3";
const adapter = new PrismaBetterSqlite3({ url: "file:./dev.db" });
const prisma = new PrismaClient({ adapter });
async function main() {
await prisma.testRecord.deleteMany();
const uint8 = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"
// Case 1: Uint8Array directly — works fine in both versions
const r1 = await prisma.testRecord.create({ data: { data: uint8 as any } });
const f1 = await prisma.testRecord.findFirst({ where: { id: r1.id } });
console.log("Direct:", JSON.stringify(f1!.data));
// Both 7.3.0 and 7.4.1: "SGVsbG8=" ✅
// Case 2: Uint8Array nested in object — BROKEN in 7.4.1
const r2 = await prisma.testRecord.create({
data: { data: { payload: uint8, label: "test" } as any },
});
const f2 = await prisma.testRecord.findFirst({ where: { id: r2.id } });
console.log("Nested in object:", JSON.stringify(f2!.data));
// 7.3.0: {"payload":"SGVsbG8=","label":"test"} ✅
// 7.4.1: {"payload":{"0":72,"1":101,"2":108,"3":108,"4":111},"label":"test"} ❌
// Case 3: Uint8Array nested in array — BROKEN in 7.4.1
const r3 = await prisma.testRecord.create({
data: { data: [uint8, "hello"] as any },
});
const f3 = await prisma.testRecord.findFirst({ where: { id: r3.id } });
console.log("Nested in array:", JSON.stringify(f3!.data));
// 7.3.0: ["SGVsbG8=","hello"] ✅
// 7.4.1: [{"0":72,"1":101,"2":108,"3":108,"4":111},"hello"] ❌
}
main()
.catch(console.error)
.finally(() => prisma.$disconnect());Steps to reproduce
npm install prisma@7.4.1 @prisma/client@7.4.1 @prisma/adapter-better-sqlite3 better-sqlite3npx prisma generatenpx prisma db push- Run the reproduction script above
- Observe that nested
Uint8Arrayvalues are stored as{"0":72,"1":101,...}instead of base64 strings
Expected vs. Actual Behavior
Expected (as in 7.3.0):
When a Uint8Array is nested inside an object or array and stored in a Json field, it should be serialized to a base64 string, consistent with how top-level Uint8Array values are handled.
Direct Uint8Array: "SGVsbG8=" ✅
Nested in object: {"payload":"SGVsbG8=","label":"test"} ✅
Nested in array: ["SGVsbG8=","hello"] ✅
Actual (in 7.4.1):
Nested Uint8Array values are expanded into objects with numeric keys via default JSON.stringify behavior, losing the base64 encoding:
Direct Uint8Array: "SGVsbG8=" ✅ (still works)
Nested in object: {"payload":{"0":72,"1":101,"2":108,"3":108,"4":111},"label":"test"} ❌ BUG
Nested in array: [{"0":72,"1":101,"2":108,"3":108,"4":111},"hello"] ❌ BUG
Raw SQLite values confirm the difference
7.3.0:
id=1: "SGVsbG8="
id=2: {"payload":"SGVsbG8=","label":"test"}
id=3: ["SGVsbG8=","hello"]
7.4.1:
id=1: "SGVsbG8="
id=2: {"payload":{"0":72,"1":101,"2":108,"3":108,"4":111},"label":"test"}
id=3: [{"0":72,"1":101,"2":108,"3":108,"4":111},"hello"]
Frequency
Consistently reproducible
Does this occur in development or production?
Both development and production
Is this a regression?
Yes. This worked correctly in Prisma 7.3.0. The regression was introduced in 7.4.1.
Workaround
Manually convert Uint8Array to a base64 string before storing it in a Json field:
function uint8ToBase64(uint8: Uint8Array): string {
return Buffer.from(uint8).toString("base64");
}
// Instead of:
await prisma.testRecord.create({
data: { data: { payload: uint8 } },
});
// Do:
await prisma.testRecord.create({
data: { data: { payload: uint8ToBase64(uint8) } },
});Prisma Schema & Queries
generator client {
provider = "prisma-client"
output = "../generated/prisma"
}
datasource db {
provider = "sqlite"
}
model TestRecord {
id Int @id @default(autoincrement())
data Json
}const uint8 = new Uint8Array([72, 101, 108, 108, 111]);
// This works fine (top-level):
await prisma.testRecord.create({ data: { data: uint8 } });
// This is broken (nested):
await prisma.testRecord.create({
data: { data: { payload: uint8, label: "test" } },
});Prisma Config
import "dotenv/config";
import { defineConfig } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
},
datasource: {
url: process.env["DATABASE_URL"], // "file:./dev.db"
},
});Logs & Debug Info
# Prisma 7.3.0 output (correct):
--- Case 1: Uint8Array directly ---
Stored: "SGVsbG8="
>> BASE64 STRING
--- Case 2: Uint8Array nested in object ---
Stored: {"payload":"SGVsbG8=","label":"test"}
>> payload is base64 string (expected)
--- Case 3: Uint8Array inside array ---
Stored: ["SGVsbG8=","hello"]
# Prisma 7.4.1 output (broken):
--- Case 1: Uint8Array directly ---
Stored: "SGVsbG8="
>> BASE64 STRING
--- Case 2: Uint8Array nested in object ---
Stored: {"payload":{"0":72,"1":101,"2":108,"3":108,"4":111},"label":"test"}
>> payload is object/array (BUG: Uint8Array expanded)
--- Case 3: Uint8Array inside array ---
Stored: [{"0":72,"1":101,"2":108,"3":108,"4":111},"hello"]
Environment & Setup
- OS: macOS 15.6 (Darwin arm64)
- Database: SQLite (via
@prisma/adapter-better-sqlite3) - Node.js version: v22.12.0
Prisma Version
prisma : 7.4.1
@prisma/client : 7.4.1
Operating System : darwin
Architecture : arm64
Node.js : v22.12.0
TypeScript : 5.9.3
Query Compiler : enabled
PSL : @prisma/prisma-schema-wasm 7.5.0-4.55ae170b1ced7fc6ed07a15f110549408c501bb3
Schema Engine : schema-engine-cli 55ae170b1ced7fc6ed07a15f110549408c501bb3
Studio : 0.13.1