prisma: uncheckedScalarInputs: XOR not working?

Bug description

The XOR functionality to allow either unchecked scalars xor regular prisma relations doesn’t appear to be working for me

the function:

export const createInvestor = ({ input: { companyId, email } }) => {
  return db.investor.create({
    data: {
      company: {
        connect: { id: companyId },
      },
      investor: {
        connectOrCreate: {
          where: { email },
          create: { email },
        },
      },
    },
  })
}

the error:

api | Error:
api | Invalid `prisma.investor.create()` invocation:
api |
api | {
api |   data: {
api |     company: {
api |     ~~~~~~~
api |       connect: {
api |         where: {
api |           companyId: 'dc7085fb-62e0-405c-bb0f-a4d33fa6bcae'
api |         }
api |       }
api |     },
api |     investor: {
api |     ~~~~~~~~
api |       connectOrCreate: {
api |         where: {
api |           email: 'test@gmail.com'
api |         },
api |         create: {
api |           email: 'test@gmail.com'
api |         }
api |       }
api |     },
api | +   investorId: String,
api | +   companyId: String,
api | ?   createdAt?: DateTime,
api | ?   updatedAt?: DateTime
api |   }
api | }
api |
api | Unknown arg `company` in data.company for type InvestorUncheckedCreateInput. Did you mean `companyId`?
api | Unknown arg `investor` in data.investor for type InvestorUncheckedCreateInput. Did you mean `investorId`?
api | Argument investorId for data.investorId is missing.
api | Argument companyId for data.companyId is missing.

Expected behavior

Prisma should recognize I’m not using unchecked scalars

Prisma information

model Investor {
  investor    User  @relation(fields: [investorId], references: [id])
  investorId    String
  company    Company  @relation(fields: [companyId], references: [id])
  companyId    String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@id([investorId, companyId])
}

Environment & setup

  • OS: MacOS 10.15.5
  • Database: Postgres 12.5
  • Node.js version: 14.15.4
  • Prisma version: 2.11.0 [used through redwoodjs]
  • Redwood version: 0.23

Happy to provide further information if helpful, have not yet tried to re-create in a minimal repo

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 26 (5 by maintainers)

Most upvoted comments

I just upgraded to 2.15 and Creates & Updates that were previously working started hitting the types <type>Unchecked<Create/Update>Input instead of expected <type><Create/Update>Input (so for example ContactPersonUncheckedUpdateInput when I’m providing data that matches ContactPersonUpdateInput.)

At first I though I had the same issue, but turns out it was actually an error on my part, causing to a bit misleading error message! So read further to check if that’s the case with you as well

Here’s some details.

prisma.schema:

model Company {
  id            Int             @id @default(autoincrement()) @db.Int
  <other-data>
}

model ContactPerson {
  id          String        @id @db.VarChar(191)
  <other-data>

  companyId   Int?          @db.Int
  Company     Company?      @relation(fields: [companyId], references: [id])
  @@index([companyId], name: "companyId")

  SubContract SubContract[]
}

Log

  create: {
    id: '18864',
    <other-data>
    Company: {
    ~~~~~~~
      connect: {
        id: '12345'
      }
    },
+   updatedAt: DateTime,
?   createdAt?: DateTime,
?   companyId?: Int | null,
?   SubContract?: {
?     create?: SubContractCreateWithoutContactPersonInput | SubContractCreateWithoutContactPersonInput | SubContractUncheckedCreateWithoutContactPersonInput | SubContractUncheckedCreateWithoutContactPersonInput,
?     connect?: SubContractWhereUniqueInput | SubContractWhereUniqueInput,
?     connectOrCreate?: SubContractCreateOrConnectWithoutContactPersonInput | SubContractCreateOrConnectWithoutContactPersonInput
?   },
...
  }
}

Unknown arg `Company` in update.Company for type ContactPersonUncheckedUpdateInput. Did you mean `companyId`?

prisma client index.ts:

export type ContactPersonCreateArgs = {
   ....
  data: XOR<ContactPersonUncheckedCreateInput, ContactPersonCreateInput>
}

  export type ContactPersonCreateInput = {
    id: string
    <other-data>
    Company?: CompanyCreateOneWithoutContactPersonsInput
    SubContract?: SubContractCreateManyWithoutContactPersonInput
  }

  export type ContactPersonUncheckedCreateInput = {
    id: string
    <other-data>
    companyId?: number | null
    SubContract?: SubContractUncheckedCreateManyWithoutContactPersonInput
  }

  export type CompanyCreateOneWithoutContactPersonsInput = {
    create?: XOR<CompanyUncheckedCreateWithoutContactPersonsInput, CompanyCreateWithoutContactPersonsInput>
    connect?: CompanyWhereUniqueInput
    connectOrCreate?: CompanyCreateOrConnectWithoutContactPersonsInput
  }

  export type CompanyWhereUniqueInput = {
    id?: number
  }

…but what had really happened that I had just refactored the Company ID to be Int instead of String, but was still providing a String in one place - causing the XOR to not match ContactPersonCreateInput, and fall back to the ContactPersonUncheckedCreateInput, leaving me on a goose hunt googling, finding this error and thinking I had the same case. 😄

So changing the prisma.create call’s data from String Company = { connect: '12345' } to number (as expected in CompanyWhereUniqueInput) Company = { connect: 12345 } caused the XOR to match the correct input type, and fixed the matter for me.

So, in case you encounter this error double check that you’re feeding the exactly right types first, since the error message is currently bit misleading.

Anyone found a way to disable this “uncheckedScalarInputs” feature totally? (since it’s not a switchable preview feature anymore since 2.15).

Or how to force using the Checked models only, I think it was mentioned in some discussion but couldn’t find instructions for it in the Prisma docs yet.

I personally don’t really see any benefit to them in my current project (I’m totally happy doing “Object: { connect: { id: id } }” instead of “objectId: id”), and they are really making debugging the actual models a PITA since the error messages are all wrong.

Hi @timsuchanek!

I think I’m running with this exact same issue.

This is the error I get when I try to create a Post. image

Weirdly enough, I’m able to Update a post with categories, but can’t Create.

Here are the informations related to the Create Post:

Create Post

Types in Prisma’s index.d.ts:

export type PostCreateInput = {
    createdAt?: Date | string
    published?: boolean
    title: string
    content?: string
    author: UserCreateNestedOneWithoutPostsInput
    categories?: CategoryCreateNestedManyWithoutPostsInput
    tags?: TagCreateNestedManyWithoutPostsInput
}

export type PostUncheckedCreateInput = {
    id?: number
    createdAt?: Date | string
    authorId: number
    published?: boolean
    title: string
    content?: string
}

Mutation:

const CreatePost = z
  .object({
    title: z.string(),
    content: z.string(),
    published: z.boolean(),
    categories: z.array(
      z.object({
        name: z.string(),
        id: z.number().int(),
      }),
    ),
  })
  .nonstrict()

export default resolver.pipe(
  resolver.zod(CreatePost),
  resolver.authorize(),
  async ({id, ...data}) => {
    const payload = {
      ...data,
      categories: {
        connectOrCreate: data.categories.map((cat) => ({
          where: {id: cat.id},
          create: {
            name: cat.name,
          },
        })),
      },
    }

    return await db.post.create({
      data: payload,
    })
  },
)

Update Post

Types in Prisma’s index.d.ts:

export type PostUpdateInput = {
    createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
    published?: BoolFieldUpdateOperationsInput | boolean
    title?: StringFieldUpdateOperationsInput | string
    content?: StringFieldUpdateOperationsInput | string
    author?: UserUpdateOneRequiredWithoutPostsInput
    categories?: CategoryUpdateManyWithoutPostsInput
    tags?: TagUpdateManyWithoutPostsInput
}

export type PostUncheckedUpdateInput = {
    id?: IntFieldUpdateOperationsInput | number
    createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
    authorId?: IntFieldUpdateOperationsInput | number
    published?: BoolFieldUpdateOperationsInput | boolean
    title?: StringFieldUpdateOperationsInput | string
    content?: StringFieldUpdateOperationsInput | string
}

Mutation:

const UpdatePost = z
  .object({
    id: z.number().int(),
    title: z.string(),
    content: z.string(),
    published: z.boolean(),
    categories: z.array(
      z.object({
        name: z.string(),
        id: z.number().int(),
      }),
    ),
  })
  .nonstrict()

export default resolver.pipe(
  resolver.zod(UpdatePost),
  resolver.authorize(),
  async ({id, ...data}) => {
    delete data.authorId

    const payload = {
      ...data,
      categories: {
        set: [],
        connectOrCreate: data.categories.map((cat) => ({
          where: {id: cat.id},
          create: {
            name: cat.name,
          },
        })),
      },
    }

    return await db.post.update({where: {id}, data: payload})
  },
)

And here is the Model Schema:

model Post {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  authorId  Int
  published Boolean  @default(false)
  title     String
  content   String   @default("")

  author     User       @relation(fields: [authorId], references: [id])
  categories Category[]
  tags       Tag[]
}

model Category {
  id   Int    @id @default(autoincrement())
  name String

  posts Post[]
}

I can’t understand why this is working in Update and not on Create when both Types are the same regarding to the categories field. Maybe I’m doing something obviously wrong. Can someone help me out?

@wminshew there is not. But in my experience when I got runtime error messages like the log from thrant I made a mistake in my code somewhere in the nested objects (i.e. using the wrong key) which can be confusing, since the error messages show the highlight the mistake in the highest layer

Best open a new issue with a full description of your situation and including the filled out issue template - much easier for us to handle @jagoncalves14 - thanks.

I am also having this issue. It defaults to assuming I’m using the unchecked type instead of the checked type.

const customerCreate: Prisma.CustomerCreateInput = {
    status: "prospect",
    user: {
      create: userCreate,
    },
    details: {
      create: customerDetailsCreate,
    },
    programs: {
      create: customerProgramCreate,
    },
  };
 const newCustomer = await ctx.db.customer.create({
    data: customerCreate,
  });

this is my schema

model Customer {
  id        String    @id @default(cuid())
  createdAt DateTime  @default(now()) @map(name: "created_at")
  updatedAt DateTime  @default(now()) @updatedAt @map(name: "updated_at")
  deletedAt DateTime? @map(name: "deleted_at")

  status String?

  userId String @unique @map(name: "user_id")
  user   User   @relation(fields: [userId], references: [id])

  details       CustomerDetail[]
  notes         CustomerAdminNote[]
  admins        Admin[]             @relation("admin_customer_link")
  events        CustomerEvent[]
  cohorts       Cohort[]            @relation("cohort_customer_link")
  programs      CustomerProgram[]
  tasks         CustomerTask[]
  applications  IntakeApplication[]
  loanCustomer  LoanCustomer?
  creditReports CreditReport[]      @relation("credit_report_customer_link")
  @@index([id], name: "customer_id_index")
  @@map(name: "customer")
}

getting this error

message: "Unknown arg `user` in data.user for type CustomerUncheckedCreateInput. Did you mean `userId`?↵Unknown arg `providerProgram` in data.programs.create.0.providerProgram for type CustomerProgramUncheckedCreateWithoutCustomerInput. Did you mean `providerProgramId`?↵Unknown arg `providerProgramCost` in data.programs.create.0.providerProgramCost for type CustomerProgramUncheckedCreateWithoutCustomerInput. Did you mean `providerProgramCostId`?↵Unknown arg `providerProgramPaymentOption` in data.programs.create.0.providerProgramPaymentOption for type CustomerProgramUncheckedCreateWithoutCustomerInput. Did you mean `providerProgramPaymentOptionId`?↵Unknown arg `cohort` in data.programs.create.0.cohort for type CustomerProgramUncheckedCreateWithoutCustomerInput. Did you mean `cohortId`?↵Argument providerProgramId for data.programs.create.0.providerProgramId is missing.↵Unknown arg `creditReports` in data.creditReports for type CustomerUncheckedCreateInput.↵Argument userId for data.userId is missing.↵"

from what I’ve seen elsewhere, if you have any error in your types it’ll only flag the first type of the XOR in the message (which, as you’ve noted, is unfortunately not very helpful)

In your case, I think the issue might be that you’re missing a connect or create or connectOrCreate. I would expect the below to work:

categories: {
  create: {
    id: 'ckkp6apog0025ukt25igvcro1'
  }
}

related to your last point, yes I don’t think these types are usable as DTOs

@nsebs nothing to bother, that’s a good question! Could you create a separate issue please? Because it seems unrelated to this one.

Yes, exactly.

Sorry about the noise in this issue.

After reading more of the docs I realized that I was trying to use the types in the wrong way:

https://www.prisma.io/docs/concepts/components/prisma-client/working-with-generated-types#problem-using-variations-of-the-generated-model-type

The PostUpdateInput and PostCreateInput are misleading because they’re not going to contain the relation with categories because is not include by default, even when it’s explicit on the signature of the type 😕

Which means that we cannot use the generated types as DTOs 😞

I’m having the same problem. I’m using NestJS.

This is the schema (part)

model Post {
  id          String      @id @default(cuid())
  createdAt   DateTime    @default(now())
  title       String
  published   Boolean     @default(false)
  author      User        @relation(fields: [authorId], references: [id])
  authorId    String
  categories  Category[]  @relation(references: [id])
}

model Category {
  id          String      @id @default(cuid())
  name        String
  posts       Post[]      @relation(references: [id])
}

This are the generated types:

  export type PostUpdateArgs = {
    select?: PostSelect | null
    include?: PostInclude | null
    data: XOR<PostUncheckedUpdateInput, PostUpdateInput>
    where: PostWhereUniqueInput
  }

This is the controller:

  @Put(':id')
  update(
    @Param('id') where: Prisma.PostWhereUniqueInput,
    @Body() data: Prisma.PostUpdateInput,
  ): Promise<Posts> {
    return this.prisma.post.update({ where, data });
  }

And this is the error thrown by prisma:

Invalid `prisma.post.create()` invocation:

{
  data: {
    title: 'Sword fighting',
    authorId: 'ckkosq7kc0014hlt2s3f18ljx',
    categories: [
    ~~~~~~~~~~
      'ckkp6apog0025ukt25igvcro1'
    ]
  }
}

Unknown arg `categories` in data.categories for type PostUncheckedCreateInput. Available args:

type PostUncheckedCreateInput {
  id?: String
  createdAt?: DateTime
  title: String
  published?: Boolean
  authorId: String
}

Prisma env:

❯ npx prisma --version     
Environment variables loaded from .env
prisma               : 2.16.0
@prisma/client       : 2.16.0
Current platform     : rhel-openssl-1.1.x
Query Engine         : query-engine 854c8ba7f0dce66f115af36af24e66989a8c02a1 (at node_modules/prisma/node_modules/@prisma/engines/query-engine-rhel-openssl-1.1.x)
Migration Engine     : migration-engine-cli 854c8ba7f0dce66f115af36af24e66989a8c02a1 (at node_modules/prisma/node_modules/@prisma/engines/migration-engine-rhel-openssl-1.1.x)
Introspection Engine : introspection-core 854c8ba7f0dce66f115af36af24e66989a8c02a1 (at node_modules/prisma/node_modules/@prisma/engines/introspection-engine-rhel-openssl-1.1.x)
Format Binary        : prisma-fmt 854c8ba7f0dce66f115af36af24e66989a8c02a1 (at node_modules/prisma/node_modules/@prisma/engines/prisma-fmt-rhel-openssl-1.1.x)
Studio               : 0.346.0

I made this project to check if the error can be reproduced and I’m always able to reproduce it when trying to create or update a model with a many-to-many relation, regardless of how that relation is defined (implicit, explicit, with or without the @relation() attribute).