Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create and connect for a nested element in a recursive structure in the same prismaClient.x.create({...}) call Panics with Expected record selection to contain required model ID fields.: FieldNotFound #23989

Open
Katli95 opened this issue Apr 26, 2024 · 4 comments
Labels
bug/2-confirmed Bug has been reproduced and confirmed. kind/bug A reported bug. team/client Issue for team Client. topic: connect topic: create nested create topic: rust panic

Comments

@Katli95
Copy link

Katli95 commented Apr 26, 2024

Bug description

I've got a Data Structure which is recursive. I'm creating an entity A1 of TypeA which creates sub entities of TypeA or TybeB, through the tree the sub entities share a new descendant A2 of TypeA (at separate depths). When I call prismaClient.typeA.create({...}) with a payload which creates A2 at it's first occurrence and then connects at it's second I'm confronted with the error:

thread 'tokio-runtime-worker' panicked at query-engine/core/src/interpreter/interpreter_impl.rs:71:26:

Expected record selection to contain required model ID fields.: FieldNotFound { name: "id", container_type: "field", container_name: "Record values: Record { values: [String(\"wqm5z06cof3kbbf4lrzk3zo9\"), String(\"clvgbsgq8000370227o22tfrq\")], parent_id: None }. Field names: [\"thingAId\", \"childUtilityId\"]." }

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

PrismaClientRustPanicError:
    Invalid `prisma.thingA.create()` invocation:

Error details formatted for readability:

// Expected record selection to contain required model ID fields.: FieldNotFound
{
    "name": "id",
    "container_type": "field",
    "container_name":
        // Record values: Record
        {
            "values": ["wqm5z06cof3kbbf4lrzk3zo9", "clvgbsgq8000370227o22tfrq"],
            "parent_id": "None",
            "Field names": ["thingAId", "childUtilityId"]
        }
}

This happens regardless of whether I create and then connect or use connectOrCreate for both instances of A2.

How to reproduce

Repro Repo
the readme has repro instructions, basically just install and a couple of clicks in browser

Expected behavior

Well, I'd expected the thing to work 😅

Formally I suppose I'd expected A2 to be inserted by the first "command" and then connected by the second without throwing an error.

Prisma information

Links to the code are present in the repro readme

model ThingA {
  id String @id @default(cuid())

  parts PartOfA[]

  referencesToMe LinkToChildUtility[]
}

model PartOfA {
  thingA   ThingA @relation(fields: [thingAId], references: [id], onDelete: NoAction, onUpdate: NoAction)
  thingAId String

  childUtilityId String             @unique
  childUtility   LinkToChildUtility @relation(fields: [childUtilityId], references: [id])

  @@id([thingAId, childUtilityId])
}

model ThingB {
  id String @id @default(cuid())

  childUtilityId String             @unique
  childUtility   LinkToChildUtility @relation(name: "thing-b-referencing-child-utility", fields: [childUtilityId], references: [id])

  referencesToMe LinkToChildUtility[] @relation(name: "child-utility-referencing-thing-b")
}

model LinkToChildUtility {
  id String @id @default(cuid())

  childAId String?
  childA   ThingA? @relation(fields: [childAId], references: [id], onDelete: NoAction, onUpdate: NoAction)

  childBId String?
  childB   ThingB? @relation(name: "child-utility-referencing-thing-b", fields: [childBId], references: [id], onDelete: NoAction, onUpdate: NoAction)

  // References to this reference

  bReferencingMe       ThingB?  @relation(name: "thing-b-referencing-child-utility")
  partOfAReferencingMe PartOfA?
}
// Simplified type & "entity overview" below

const idOfCreatedToConnect = "the-thing-to-be-connected";
const result = await prisma.thingA.create({
    data: {
        id: "xojzl4x26txb0tt1vztdakdd",
        parts: {
            create: [
                {
                    childUtility: {
                        create: {
                            childA: {
                                // Here the thing should be created
                                // originally we used `create` but tried `connectOrCreate` as a fallback
                                // it didn't work so maybe we'll go back to create here
                                connectOrCreate: {
                                    where: { id: idOfCreatedToConnect },
                                    create: {
                                        id: idOfCreatedToConnect,
                                    },
                                },
                            },
                        },
                    },
                },
                {
                    childUtility: {
                        create: {
                            childB: {
                                connectOrCreate: {
                                    where: { id: "v3h22nh8cw46imjkspl2w2d0" },
                                    create: {
                                        id: "v3h22nh8cw46imjkspl2w2d0",
                                        childUtility: {
                                            create: {
                                                childA: {
                                                    connectOrCreate: {
                                                        where: { id: "wqm5z06cof3kbbf4lrzk3zo9" },
                                                        create: {
                                                            id: "wqm5z06cof3kbbf4lrzk3zo9",
                                                            parts: {
                                                                create: [
                                                                    {
                                                                        childUtility: {
                                                                            create: {
                                                                                childB: {
                                                                                    connectOrCreate: {
                                                                                        where: {
                                                                                            id: "eokkmtd95f2nq6dyzfyeplpg",
                                                                                        },
                                                                                        create: {
                                                                                            id: "eokkmtd95f2nq6dyzfyeplpg",
                                                                                            childUtility: {
                                                                                                create: {
                                                                                                    // Here the thing should be connected
                                                                                                    // again originally we used `connect` but tried `connectOrCreate` as a fallback
                                                                                                    // it didn't work so maybe we'll go back to connect here
                                                                                                    childA: {
                                                                                                        connectOrCreate:
                                                                                                            {
                                                                                                                where: {
                                                                                                                    id: idOfCreatedToConnect,
                                                                                                                },
                                                                                                                create: {
                                                                                                                    id: idOfCreatedToConnect,
                                                                                                                },
                                                                                                            },
                                                                                                    },
                                                                                                },
                                                                                            },
                                                                                        },
                                                                                    },
                                                                                },
                                                                            },
                                                                        },
                                                                    },
                                                                ],
                                                            },
                                                        },
                                                    },
                                                },
                                            },
                                        },
                                    },
                                },
                            },
                        },
                    },
                },
            ],
        },
    },
});

// The above is basically inserting the following type & entity:
type A = {
    parts: {
        childUtility: {
            childA?: {
                // At this part we could recursively select further but our use case doesn't require it
                id: string;
            };
            childB?: {
                // In our project we don't even select "this far down", we'd just select the ID,
                // but I'm including this so you can see the effective shape of ThingB
                childUtility: {
                    childA?: {
                        id: string;
                    } & Partial<A>;
                    childB?: {
                        id: string;
                    };
                };
            };
        };
    }[];
};

const idOfNewNestedA = "the-thing-to-be-connected";
const newThingA: A = {
    parts: [
        {
            childUtility: {
                childA: { id: idOfNewNestedA },
            },
        },
        {
            childUtility: {
                childB: {
                    childUtility: {
                        childA: {
                            parts: [
                                {
                                    childUtility: {
                                        childB: { childUtility: { childA: { id: idOfNewNestedA } } },
                                    },
                                },
                            ],
                        },
                    },
                },
            },
        },
    ],
};

Environment & setup

  • OS: macOS
  • Database: sqlite
  • Node.js: v20.11.0

Prisma Version

npx prisma -v                   
prisma                  : 5.13.0
@prisma/client          : 5.13.0
Computed binaryTarget   : darwin-arm64
Operating System        : darwin
Architecture            : arm64
Node.js                 : v20.11.0
Query Engine (Node-API) : libquery-engine b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b (at node_modules/@prisma/engines/libquery_engine-darwin-arm64.dylib.node)
Schema Engine           : schema-engine-cli b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b (at node_modules/@prisma/engines/schema-engine-darwin-arm64)
Schema Wasm             : @prisma/prisma-schema-wasm 5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b
Default Engines Hash    : b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b
Studio                  : 0.500.0
@Katli95 Katli95 added the kind/bug A reported bug. label Apr 26, 2024
@Katli95 Katli95 changed the title Expected record selection to contain required model ID fields.: FieldNotFound { name: "id", container_type: "field", container_name: "Record values: Record { values: [String(\"wqm5z06cof3kbbf4lrzk3zo9\"), String(\"clvgcyphp0003wowt1yto7es6\")], parent_id: None }. Field names: [\"thingAId\", \"childUtilityId\"]." } create and connect for a nested element in a recursive structure in the same prismaClient.x.create({...}) call Panics with Expected record selection to contain required model ID fields.: FieldNotFound Apr 26, 2024
@SevInf SevInf added bug/1-unconfirmed Bug should have enough information for reproduction, but confirmation has not happened yet. team/client Issue for team Client. topic: rust panic topic: create nested create labels Apr 29, 2024
@Katli95
Copy link
Author

Katli95 commented May 2, 2024

Sorry for the insistence, I'm sure you guys have enough on your plates 😅

It seems the error is being thrown from the core interpreter here

// We always select IDs, the unwraps are safe.
QueryResult::RecordSelection(Some(rs)) => Some(
    rs.records
        .extract_selection_results_from_db_name(field_selection)
        .expect("Expected record selection to contain required model ID fields.")
        .into_iter()
        .collect(),
),

Unfortunately I'm not familiar enough with the engines to dive deep and fix it myself as is, could someone guess when/if the team might get to this ticket or maybe guide me towards where to start looking if I decided to try and make a PR myself?

Also, should I keep this issue or close it and open a new one in prisma/prisma-engines?

@janpio
Copy link
Member

janpio commented May 6, 2024

Also, should I keep this issue or close it and open a new one in prisma/prisma-engines?

No, this is the correct place for it.

Unfortunately I'm not familiar enough with the engines to dive deep and fix it myself as is, could someone guess when/if the team might get to this ticket or maybe guide me towards where to start looking if I decided to try and make a PR myself?

Next step is for someone on our side to reproduce the panic you are seeing, then we can prioritize fixing this. As you can see we have a lot of issues open, and many affect hundreds of users. So unfortunately think this will not get get an immediate reaction on our side.

@janpio janpio added bug/2-confirmed Bug has been reproduced and confirmed. and removed bug/1-unconfirmed Bug should have enough information for reproduction, but confirmation has not happened yet. labels May 6, 2024
@janpio
Copy link
Member

janpio commented May 6, 2024

I can reproduce the panic with the provided, excellent repro repository:

 ✓ Compiled /test in 186ms (863 modules)
 ✓ Compiled /api/test in 327ms (498 modules)
thread 'tokio-runtime-worker' panicked at query-engine/core/src/interpreter/interpreter_impl.rs:71:26:
Expected record selection to contain required model ID fields.: FieldNotFound { name: "id", container_type: "field", container_name: "Record values: Record { values: [String(\"wqm5z06cof3kbbf4lrzk3zo9\"), String(\"clvutwecc00039rm75pl6kd58\")], parent_id: None }. Field names: [\"thingAId\", \"childUtilityId\"]." }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
PrismaClientRustPanicError: 
Invalid `prisma.thingA.create()` invocation:


Expected record selection to contain required model ID fields.: FieldNotFound { name: "id", container_type: "field", container_name: "Record values: Record { values: [String(\"wqm5z06cof3kbbf4lrzk3zo9\"), String(\"clvutwecc00039rm75pl6kd58\")], parent_id: None }. Field names: [\"thingAId\", \"childUtilityId\"]." }

@Katli95
Copy link
Author

Katli95 commented May 6, 2024

As you can see we have a lot of issues open, and many affect hundreds of users. So unfortunately think this will not get get an immediate reaction on our side.

No hahah, yeah, I'd assumed as much, so thanks for just replying in general!

I wanna ask again if someone could maybe guide me towards where & how to start looking so that I might attempt fixing it myself?

  1. I'm assuming the engine readme has everything needed to get my env up & running
  2. I'm not seeing at a glance any documentation for how the engine is structured or how to start looking for the bug I'm running into. So just a hint to where:
    1. The selects are automatically generated for a given nested query would be awesome (I'm assuming this is done somewhere)
    2. In lieu of that, where I can find the code "running behind" prisma.anyModel.create()?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug/2-confirmed Bug has been reproduced and confirmed. kind/bug A reported bug. team/client Issue for team Client. topic: connect topic: create nested create topic: rust panic
Projects
None yet
Development

No branches or pull requests

3 participants