drizzle-team / drizzle-team/drizzle-orm

[BUG]: TypeError: Cannot read properties of undefined (reading 'replace')

Open
#3,766 30 comments 15 reactions 0 assignees View on GitHub
bug driver/supabase drizzle/kit priority
Dominant language
TypeScript
Stars
35.8k
Forks
1.6k
Avg merge
2d 7h
Merged PRs (30d)
4

Description

### Report hasn't been filed before.

- [X] I have verified that the bug I'm about to report hasn't been filed before.

### What version of `drizzle-orm` are you using?

^0.38.2

### What version of `drizzle-kit` are you using?

^0.30.1

### Other packages

_No response_

### Describe the Bug

I'm switching from Option 3 to Option 2 from this document https://orm.drizzle.team/docs/migrations

I used `drizzle-kit push` to push my new schema changes to Supabase database but got this error
```
.../node_modules/drizzle-kit/bin.cjs:20104
checkValue = checkValue.replace(/^CHECK\s*\(\(/, "").replace(/\)\)\s*$/, "");
^
TypeError: Cannot read properties of undefined (reading 'replace')
Node.js v20.13.1
```
Here's my schema
```
export const visibilityEnum = pgEnum("visibility", [
"public",
"private",
"exclusive",
]);
export const videoStatusEnum = pgEnum("video_status", [
"draft",
"scheduled",
"processing",
"deleted",
"banned",
"active",
]);

export const videosTable = pgTable("videos", {
id: uuid("id").defaultRandom().primaryKey(),
creator_id: uuid("creator_id")
.references(() => authUsersTable.id)
.notNull(),
channel_id: uuid("channel_id")
.references(() => channelsTable.id)
.notNull(),
hosting_video_id: text("hosting_video_id").default("").notNull(),
hosting_provider: text("hosting_provider").default("").notNull(),
title: text("title").notNull(),
description: text("description"),
thumbnail_url: text("thumbnail_url").default("").notNull(),
preview_animation_url: text("preview_animation_url").default("").notNull(),
hls_url: text("hls_url").default("").notNull(),
embed_url: text("embed_url").default("").notNull(),
view_count: integer("view_count").default(0).notNull(),
like_count: integer("like_count").default(0).notNull(),
original_size_bytes: integer("original_size_bytes").notNull(),
storage_size_bytes: integer("storage_size_bytes").notNull(),
visibility: visibilityEnum("visibility").default("public").notNull(),
status: videoStatusEnum("status").default("draft").notNull(),
created_at: timestamp("created_at", { withTimezone: true })
.defaultNow()
.notNull(),
});

export const videoRelations = relations(videosTable, ({ one, many }) => ({
channel: one(channelsTable, {
fields: [videosTable.channel_id],
references: [channelsTable.id],
}),
quizzes: many(quizzesTable),
}));

export const actionTypeEnum = pgEnum("action_type", ["like", "share"]);

export const videoActionsTable = pgTable(
"video_actions",
{
id: serial("id").primaryKey(),
user_id: uuid("user_id")
.references(() => authUsersTable.id)
.notNull(),
video_id: uuid("video_id")
.references(() => videosTable.id)
.notNull(),
action_type: actionTypeEnum("action_type").default("like").notNull(),
created_at: timestamp("created_at", { withTimezone: true })
.defaultNow()
.notNull(),
},
(table) => [
{
uniqueUserVideo: unique("user_video_unique").on(
table.user_id,
table.video_id
),
},
]
);

export const userVideoRelations = relations(videoActionsTable, ({ one }) => ({
video: one(videosTable, {
fields: [videoActionsTable.video_id],
references: [videosTable.id],
}),
user: one(authUsersTable, {
fields: [videoActionsTable.user_id],
references: [authUsersTable.id],
}),
}));

export const userVideoProcessesTable = pgTable("user_video_processes", {
id: serial("id").primaryKey(),
user_id: uuid("user_id")
.references(() => authUsersTable.id)
.notNull(),
video_id: uuid("video_id")
.references(() => videosTable.id)
.notNull(),
study_algorithm_id: integer("study_algorithm_id")
.references(() => studyAlgorithmsTable.id)
.notNull(),
study_algorithm_stage_id: integer("study_algorithm_stage_id")
.references(() => studyAlgorithmStagesTable.id)
.notNull(),
last_reviewed_at: timestamp("last_reviewed_at", {
withTimezone: true,
}).notNull(),
next_review_at: timestamp("next_review_at", { withTimezone: true }).notNull(),
});

export const userVideoProcessRelations = relations(
userVideoProcessesTable,
({ one }) => ({
video: one(videosTable, {
fields: [userVideoProcessesTable.video_id],
references: [videosTable.id],
}),
user: one(authUsersTable, {
fields: [userVideoProcessesTable.user_id],
references: [authUsersTable.id],
}),
})
);

const authSchema = pgSchema("auth");

export const authUsersTable = authSchema.table("users", {
id: uuid("id").primaryKey(),
});

export const userProfilesTable = pgTable(
"user_profiles",
{
user_id: uuid("user_id")
.primaryKey()
.references(() => authUsersTable.id, { onDelete: "cascade" }),
email: text("email").notNull(),
name: text("name").notNull(),
avatar_url: text("avatar_url"),
fcm_token: text("fcm_token").default(""),
is_anonymous: boolean("is_anonymous").notNull().default(false),
is_premium: boolean("is_premium").notNull().default(false),
subscription_id: text("subscription_id"),
customer_id: text("customer_id"),
created_at: timestamp("created_at", { withTimezone: true })
.defaultNow()
.notNull(),
},
(t) => [
pgPolicy("Enable auth admin to read user profiles", {
as: "permissive",
to: supabaseAuthAdminRole,
for: "select",
using: sqltrue,
}),

pgPolicy("Enable auth admin to insert into user_profiles", {
as: "permissive",
to: supabaseAuthAdminRole,
for: "insert",
withCheck: sqltrue,
}),

pgPolicy("Enable auth admin to update user_profiles", {
as: "permissive",
to: supabaseAuthAdminRole,
for: "update",
using: sqltrue,
}),

pgPolicy("Enable insert for users based on user_id", {
as: "permissive",
to: anonRole,
for: "insert",
withCheck: sql(SELECT auth.uid() AS uid) = ${t.user_id},
}),

pgPolicy("Enable update for users based on user_id", {
as: "permissive",
to: anonRole,
for: "update",
using: sql(SELECT auth.uid() AS uid) = ${t.user_id},
withCheck: sql(SELECT auth.uid() AS uid) = ${t.user_id},
}),
]
);

export const studyAlgorithmsTable = pgTable("study_algorithms", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
description: text("description"),
created_at: timestamp("created_at", { withTimezone: true })
.defaultNow()
.notNull(),
});

export const studyAlgorithmStagesTable = pgTable("study_algorithm_stages", {
id: serial("id").primaryKey(),
study_algorithm_id: integer("study_algorithm_id")
.references(() => studyAlgorithmsTable.id)
.notNull(),
name: text("name").notNull(),
interval_days: integer("interval_days").notNull(),
order: integer("order").notNull(),
is_final: boolean("is_final").notNull(),
});

export const quizTypeEnum = pgEnum("quiz_type", [
"multiple-choice",
"fill-in-the-blank",
"audio-question-multiple-choice",
"audio-question-fill-in-the-blank",
]);

export const quizzesTable = pgTable("quizzes", {
id: uuid("id").defaultRandom().primaryKey(),
video_id: uuid("video_id").references(() => videosTable.id),
type: quizTypeEnum("type").notNull(),
question: text("question").notNull(),
question_audio_url: text("question_audio_url"),
correct_answer: text("correct_answer"),
start_time_seconds: integer("start_time_seconds").default(0),
created_at: timestamp("created_at", { withTimezone: true }).defaultNow(),
updated_at: timestamp("updated_at", { withTimezone: true }).defaultNow(),
});

export const quizRelations = relations(quizzesTable, ({ one, many }) => ({
video: one(videosTable, {
fields: [quizzesTable.video_id],
references: [videosTable.id],
}),
options: many(quizOptionsTable),
}));

export const quizOptionsTable = pgTable("quiz_options", {
id: uuid("id").defaultRandom().primaryKey(),
quiz_id: uuid("quiz_id")
.references(() => quizzesTable.id, { onDelete: "cascade" })
.notNull(),
option_text: text("option_text").notNull(),
is_correct: boolean("is_correct").notNull(),
});

export const quizOptionRelations = relations(quizOptionsTable, ({ one }) => ({
quiz: one(quizzesTable, {
fields: [quizOptionsTable.quiz_id],
references: [quizzesTable.id],
}),
}));

export const quizAnswersTable = pgTable("quiz_answers", {
id: serial("id").primaryKey(),
quiz_id: uuid("quiz_id")
.references(() => quizzesTable.id, { onDelete: "cascade" })
.notNull(),
user_id: uuid("user_id")
.references(() => authUsersTable.id)
.notNull(),
selected_answer: integer("selected_answer").references(
() => quizOptionsTable.id,
{ onDelete: "set null" }
),
user_answer: text("user_answer"),
is_correct: boolean("is_correct").notNull(),
created_at: timestamp("created_at", { withTimezone: true }).defaultNow(),
});

export const channelsTable = pgTable("channels", {
id: uuid("id").defaultRandom().primaryKey(),
creator_id: uuid("creator_id")
.references(() => authUsersTable.id)
.notNull(),
name: text("name").default("").notNull(),
banner_url: text("banner_url"),
profile_img_url: text("profile_img_url"),
description: text("description"),
subscriber_count: integer("subscriber_count").default(0),
is_verified: boolean("is_verified").default(false),
handle: text("handle").default("").notNull(),
links: jsonb("links"),
created_at: timestamp("created_at", { withTimezone: true })
.defaultNow()
.notNull(),
});

export const channelRelations = relations(channelsTable, ({ many }) => ({
videos: many(videosTable),
}));

export const subscriptionTypeEnum = pgEnum("subscription_type", [
"free",
"paid",
]);

export const channelSubscriptionsTable = pgTable("channel_subscriptions", {
id: serial("id").primaryKey(),
user_id: uuid("user_id")
.references(() => authUsersTable.id)
.notNull(),
channel_id: uuid("channel_id")
.references(() => channelsTable.id)
.notNull(),
subscription_type: subscriptionTypeEnum("subscription_type")
.default("free")
.notNull(),
created_at: timestamp("created_at", { withTimezone: true })
.defaultNow()
.notNull(),
});
```
and here's drizzle.config.ts. The database url is correct
```
import { defineConfig } from "drizzle-kit";

export default defineConfig({
dialect: "postgresql",
schemaFilter: ["public"],
introspect: {
casing: "preserve",
},
schema: "./src/db/schema",
out: "./supabase/migrations",
dbCredentials: {
url: process.env.DATABASE_URL ?? "",
},
});
```

Contributor guide

Open the contributing guide

Research direction

The failure is reported in drizzle-kit/bin.cjs at line 20104; start by reproducing `drizzle-kit push` with the schema under `./src/db/schema` and `drizzle.config.ts`. Compare the PostgreSQL schema constructs involved, especially policies and CHECK handling, using the reported TypeError as the regression signal. Done means the same schema push completes without the undefined `replace` failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, postgresql, typescript
Domain
cli, database, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.