Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix: correctly manage assigned values for lazy one-to-many relations#12021

Open
gioboa wants to merge 6 commits intotypeorm:masterfrom
gioboa:fix/10190
Open

fix: correctly manage assigned values for lazy one-to-many relations#12021
gioboa wants to merge 6 commits intotypeorm:masterfrom
gioboa:fix/10190

Conversation

@gioboa
Copy link
Collaborator

@gioboa gioboa commented Feb 20, 2026

Close #10190

Description of change

Pull-Request Checklist

  • Code is up-to-date with the master branch
  • This pull request links relevant issues as Fixes #00000
  • There are new or updated tests validating the change (tests/**.test.ts)
  • Documentation has been updated to reflect this change (docs/docs/**.md)

@qodo-free-for-open-source-projects

User description

Close #10190

Description of change

Pull-Request Checklist

  • Code is up-to-date with the master branch
  • This pull request links relevant issues as Fixes #00000
  • There are new or updated tests validating the change (tests/**.test.ts)
  • Documentation has been updated to reflect this change (docs/docs/**.md)

PR Type

Bug fix


Description

  • Fix lazy one-to-many relation value assignment handling

  • Add property descriptor check for assigned lazy relation values

  • Enhance JSDoc documentation with missing parameter annotations

  • Add comprehensive test coverage for lazy and non-lazy relations


Diagram Walkthrough

flowchart LR
  A["Lazy Relation Assignment"] --> B["getEntityValue Method"]
  B --> C["Property Descriptor Check"]
  C --> D["Return Assigned Value"]
  E["Test Cases"] --> F["Non-lazy Relations"]
  E --> G["Lazy Relations"]
  F --> H["Verify User ID Update"]
  G --> H
Loading

File Walkthrough

Relevant files
Bug fix
RelationMetadata.ts
Add descriptor check for lazy relation values                       

src/metadata/RelationMetadata.ts

  • Add property descriptor check in getEntityValue method to handle
    assigned lazy relation values
  • Check if property has a non-function getter before returning undefined
  • Return the assigned value directly when it exists as own property
  • Add missing JSDoc parameter annotations for multiple methods
+19/-0   
Tests
LazyPost.ts
Add LazyPost test entity                                                                 

test/functional/persistence/one-to-many-lazy/entity/LazyPost.ts

  • Create new test entity with lazy many-to-one relation
  • Define user property as Promise type for lazy loading
  • Include primary key, name column, and foreign key reference
+24/-0   
LazyUser.ts
Add LazyUser test entity                                                                 

test/functional/persistence/one-to-many-lazy/entity/LazyUser.ts

  • Create new test entity with lazy one-to-many relation
  • Define posts property as Promise array for lazy loading
  • Include primary key and name column
+14/-0   
Post.ts
Add Post test entity                                                                         

test/functional/persistence/one-to-many-lazy/entity/Post.ts

  • Create test entity for non-lazy many-to-one relation
  • Define standard user property without Promise wrapper
  • Include primary key, name, and foreign key columns
+24/-0   
User.ts
Add User test entity                                                                         

test/functional/persistence/one-to-many-lazy/entity/User.ts

  • Create test entity for non-lazy one-to-many relation
  • Define standard posts property as array without Promise
  • Include primary key and name column
+14/-0   
persistence-one-to-many-lazy.test.ts
Add comprehensive one-to-many lazy relation tests               

test/functional/persistence/one-to-many-lazy/persistence-one-to-many-lazy.test.ts

  • Add test suite for issue Cannot save a one-to-many relation #10190 covering one-to-many lazy relations
  • Test non-lazy relation: create post, user, assign post to user, verify
    user ID update
  • Test lazy relation: same scenario with Promise-based lazy relations
  • Verify that assigned relation values are properly persisted to
    database
+97/-0   

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 20, 2026

commit: 7b4131a

@coveralls
Copy link

coveralls commented Feb 20, 2026

Coverage Status

coverage: 81.478% (+0.03%) from 81.447%
when pulling 7b4131a on gioboa:fix/10190
into 42819be on typeorm:master.

@gioboa
Copy link
Collaborator Author

gioboa commented Feb 21, 2026

/review

@qodo-free-for-open-source-projects

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Edge Cases

The new lazy-relation handling relies on inspecting the entity’s own property descriptor to detect assigned values (including Promises) without triggering lazy loading. Please validate behavior for accessor-based lazy relations (properties defined via getter/setter), inherited properties (defined on the prototype, not as an “own” property), and cases where a relation property is explicitly set to null vs undefined.

    const ownDescriptor = Object.getOwnPropertyDescriptor(
        entity,
        this.propertyName,
    )
    if (
        ownDescriptor &&
        "value" in ownDescriptor &&
        typeof ownDescriptor.get !== "function"
    ) {
        if (
            ownDescriptor.value instanceof Promise &&
            !getLazyRelationsPromiseValue
        ) {
            return undefined
        }
        return ownDescriptor.value
    }

    return undefined
}
Behavior Change

The added logic changes what getEntityValue returns for lazy relations when an own data-property exists on the entity. Confirm this doesn’t break scenarios where userland code assigns a resolved array/object directly to a lazy relation property (instead of the internal __{propertyName}__ field), and ensure downstream persistence logic treats the returned value consistently (especially for one-to-many arrays).

        const ownDescriptor = Object.getOwnPropertyDescriptor(
            entity,
            this.propertyName,
        )
        if (
            ownDescriptor &&
            "value" in ownDescriptor &&
            typeof ownDescriptor.get !== "function"
        ) {
            if (
                ownDescriptor.value instanceof Promise &&
                !getLazyRelationsPromiseValue
            ) {
                return undefined
            }
            return ownDescriptor.value
        }

        return undefined
    }
    return entity[this.propertyName]
}
Test Coverage

The new tests cover assigning Promise.resolve([...]) to a lazy one-to-many and verifying the inverse foreign key update. Consider also validating the “direct assignment” variant (assigning an array directly to a lazy relation property, if supported/expected) and verifying that unresolved Promises do not accidentally trigger updates or are handled deterministically across drivers.

it("should update the user id of a post when the user is saved with a list of posts that already exist in the database", () =>
    Promise.all(
        dataSources.map(async (dataSource) => {
            const entityManager = dataSource.manager

            // Create a post without a user
            const post = new Post()
            post.id = "post-id"
            post.name = "Post"
            const savedPost = await entityManager.save(post)

            // Create a user
            const user = new User()
            user.id = "user-id"
            user.name = "John"
            await entityManager.save(user)

            // Add the post to the user
            const fetchedUser = await entityManager.findOneOrFail(User, {
                where: { id: user.id },
            })
            fetchedUser.posts = [savedPost]
            await entityManager.save(fetchedUser)

            // Check that the post has the user id
            const updatedPost = await entityManager.findOneOrFail(Post, {
                where: { name: "Post" },
            })
            expect(updatedPost.userId).to.equal(user.id)
        }),
    ))

it("should update the user id of a post when the user is saved with a list of posts that already exist in the database (with lazy relations)", () =>
    Promise.all(
        dataSources.map(async (dataSource) => {
            const entityManager = dataSource.manager

            // Create a post without a user
            const post = new LazyPost()
            post.id = "post-id"
            post.name = "Lazy Post"
            const savedPost = await entityManager.save(post)

            // Create a user
            const user = new LazyUser()
            user.id = "user-id"
            user.name = "Lazy John"
            await entityManager.save(user)

            // Add the post to the user
            const fetchedUser = await entityManager.findOneOrFail(
                LazyUser,
                {
                    where: { id: user.id },
                },
            )
            fetchedUser.posts = Promise.resolve([savedPost])
            await entityManager.save(fetchedUser)

            // Check that the post has the user id
            const updatedPost = await entityManager.findOneOrFail(
                LazyPost,
                {
                    where: { name: "Lazy Post" },
                },
            )
            expect(updatedPost.userId).to.equal(user.id)
        }),
    ))

@qodo-free-for-open-source-projects
Copy link

qodo-free-for-open-source-projects bot commented Feb 23, 2026

PR Code Suggestions ✨

Latest suggestions up to 7b4131a

CategorySuggestion                                                                                                                                    Impact
Possible issue
Prevent invoking lazy getters

To prevent accidentally triggering lazy-relation getters, refactor the logic to
use Object.getOwnPropertyDescriptor to inspect properties and retrieve their
values without invoking accessors.

src/metadata/RelationMetadata.ts [482-500]

-let hasGetter = false
 let proto: object | null = entity
 while (proto) {
-    if (
-        Object.getOwnPropertyDescriptor(
-            proto,
-            this.propertyName,
-        )?.get
-    ) {
-        hasGetter = true
-        break
+    const desc = Object.getOwnPropertyDescriptor(proto, this.propertyName)
+    if (desc) {
+        // Getter-based lazy relation: don't touch it
+        if (typeof desc.get === "function") return undefined
+
+        // Data property: safe to read without invoking getters
+        const value = (desc as PropertyDescriptor).value
+        return value instanceof Promise ? undefined : value
     }
     proto = Object.getPrototypeOf(proto)
 }
 
-if (hasGetter || entity[this.propertyName] instanceof Promise) {
-    return undefined
-}
-return entity[this.propertyName]
+return undefined
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that accessing entity[this.propertyName] can unintentionally trigger a lazy-loading getter, which the PR aims to avoid. The proposed change is more robust and correct by using Object.getOwnPropertyDescriptor to safely inspect the property without invoking getters.

Medium
  • More

Previous suggestions

✅ Suggestions up to commit 0da8107
CategorySuggestion                                                                                                                                    Impact
Possible issue
Prevent lazy getter side effects
Suggestion Impact:Updated getter detection to iterate up the prototype chain using Object.getOwnPropertyDescriptor, aligning with the suggestion's approach for getter discovery; however the code still reads entity[this.propertyName] in the Promise check.

code diff:

-                const hasGetter =
-                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
-                        ?.get ||
-                    Object.getOwnPropertyDescriptor(
-                        Object.getPrototypeOf(entity),
-                        this.propertyName,
-                    )?.get
+                let hasGetter = false
+                let proto: object | null = entity
+                while (proto) {
+                    if (
+                        Object.getOwnPropertyDescriptor(
+                            proto,
+                            this.propertyName,
+                        )?.get
+                    ) {
+                        hasGetter = true
+                        break
+                    }
+                    proto = Object.getPrototypeOf(proto)
+                }
 
                 if (hasGetter || entity[this.propertyName] instanceof Promise) {

To prevent unintended lazy-loading, first check for a getter on the prototype
chain without accessing the property value, and only if no getter exists, read
the value to check if it is a promise.

src/metadata/RelationMetadata.ts [482-493]

-const hasGetter =
-    Object.getOwnPropertyDescriptor(entity, this.propertyName)
-        ?.get ||
-    Object.getOwnPropertyDescriptor(
-        Object.getPrototypeOf(entity),
-        this.propertyName,
-    )?.get
+const hasGetter = (() => {
+    let proto: any = entity
+    while ((proto = Object.getPrototypeOf(proto))) {
+        const desc = Object.getOwnPropertyDescriptor(proto, this.propertyName)
+        if (desc?.get) return true
+    }
+    return false
+})()
 
-if (hasGetter || entity[this.propertyName] instanceof Promise) {
-    return undefined
-}
-return entity[this.propertyName]
+if (hasGetter) return undefined
 
+const value = entity[this.propertyName]
+if (InstanceChecker.isPromise(value)) return undefined
+
+return value
+
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical flaw where checking entity[this.propertyName] instanceof Promise will itself trigger a lazy-relation's getter, causing an unintended side-effect that the PR is trying to avoid.

High
Detect inherited accessor getters
Suggestion Impact:Replaced the limited two-level getter check with a loop that walks up the entire prototype chain and detects any accessor getter for the property.

code diff:

-                const hasGetter =
-                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
-                        ?.get ||
-                    Object.getOwnPropertyDescriptor(
-                        Object.getPrototypeOf(entity),
-                        this.propertyName,
-                    )?.get
+                let hasGetter = false
+                let proto: object | null = entity
+                while (proto) {
+                    if (
+                        Object.getOwnPropertyDescriptor(
+                            proto,
+                            this.propertyName,
+                        )?.get
+                    ) {
+                        hasGetter = true
+                        break
+                    }
+                    proto = Object.getPrototypeOf(proto)
+                }

Improve getter detection by traversing the entire prototype chain to reliably
find inherited accessors, as the current code only checks the instance and its
immediate prototype.

src/metadata/RelationMetadata.ts [482-488]

-const hasGetter =
-    Object.getOwnPropertyDescriptor(entity, this.propertyName)
-        ?.get ||
-    Object.getOwnPropertyDescriptor(
-        Object.getPrototypeOf(entity),
-        this.propertyName,
-    )?.get
+const hasGetter = (() => {
+    let proto: any = entity
+    while ((proto = Object.getPrototypeOf(proto))) {
+        const desc = Object.getOwnPropertyDescriptor(proto, this.propertyName)
+        if (desc?.get) return true
+    }
+    return false
+})()
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that the getter detection is incomplete as it does not traverse the full prototype chain, and the proposed fix makes the implementation more robust.

Medium
✅ Suggestions up to commit 7688c12
CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix lazy relation getter detection
Suggestion Impact:The commit updated the logic to inspect both the entity's own property descriptor and its prototype's descriptor for a getter, incorporating prototype-based accessor detection as suggested (though the final conditional behavior differs from the suggested logic).

code diff:

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
+
+                if (hasGetter || entity[this.propertyName] instanceof Promise) {
+                    return undefined
                 }
-
-                return undefined
+                return entity[this.propertyName]

Improve lazy relation getter detection by checking the entity's prototype in
addition to its own properties to correctly identify accessor-based promises.

src/metadata/RelationMetadata.ts [482-491]

-if (
-    !Object.getOwnPropertyDescriptor(
-        entity,
-        this.propertyName,
-    )?.get &&
-    entity[this.propertyName] instanceof Promise
-) {
+const ownDescriptor = Object.getOwnPropertyDescriptor(
+    entity,
+    this.propertyName,
+)
+const protoDescriptor = Object.getOwnPropertyDescriptor(
+    Object.getPrototypeOf(entity),
+    this.propertyName,
+)
+const hasAccessor = !!(ownDescriptor?.get || protoDescriptor?.get)
+
+if (!hasAccessor && entity[this.propertyName] instanceof Promise) {
     return undefined
 }
+
 return entity[this.propertyName]

[Suggestion processed]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical flaw in the PR's logic, where Object.getOwnPropertyDescriptor fails to check the prototype for lazy relation getters, and provides a robust fix.

High

@qodo-free-for-open-source-projects

PR Code Suggestions ✨

No code suggestions found for the PR.

@qodo-free-for-open-source-projects
Copy link

qodo-free-for-open-source-projects bot commented Feb 23, 2026

Code Review by Qodo

🐞 Bugs (13) 📘 Rule violations (6) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Null-proto getter check throws🐞 Bug ⛯ Reliability
Description
The new lazy-relation getter detection calls Object.getOwnPropertyDescriptor on
Object.getPrototypeOf(entity) without guarding against a null prototype, which can throw a TypeError
and break persistence/transform flows operating on plain objects.
Code

src/metadata/RelationMetadata.ts[R482-488]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
Evidence
RelationMetadata.getEntityValue now unconditionally passes Object.getPrototypeOf(entity) into
Object.getOwnPropertyDescriptor; if prototype is null, that call throws. TypeORM code explicitly
considers Object.create(null) objects as “plain objects”, and relation.getEntityValue is invoked on
user-provided plain objects during transformation/merge operations, making this a plausible runtime
crash.

src/metadata/RelationMetadata.ts[475-493]
src/util/OrmUtils.ts[538-545]
src/query-builder/transformer/PlainObjectToNewEntityTransformer.ts[56-64]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` calls `Object.getOwnPropertyDescriptor(Object.getPrototypeOf(entity), ...)` without checking if the prototype is `null`. For `Object.create(null)`-style objects, `Object.getPrototypeOf(entity)` returns `null`, and `Object.getOwnPropertyDescriptor(null, ...)` throws a `TypeError`.
## Issue Context
TypeORM utilities explicitly treat `Object.create(null)` objects as valid “plain objects”, and `relation.getEntityValue(...)` is called on user-provided plain objects in transformers.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[482-488]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. getEntityValue triggers lazy load🐞 Bug ⛯ Reliability
Description
The new lazy-relation branch reads entity[this.propertyName] (including inside an `instanceof
Promise` check), which can invoke the lazy getter and start DB queries unexpectedly, and can also
return a Promise to callers that expect concrete relation values or undefined. This can cause
unexpected queries during initialization/validation and break persistence/transform flows when an
unresolved Promise is returned.
Code

src/metadata/RelationMetadata.ts[R482-491]

+                if (
+                    !Object.getOwnPropertyDescriptor(
+                        entity,
+                        this.propertyName,
+                    )?.get &&
+                    entity[this.propertyName] instanceof Promise
+                ) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
RelationLoader.enableLazyLoad defines a getter for lazy relations that calls
relationLoader.load(...) (DB query) when the property is accessed. EntityMetadata.create applies
this lazy getter to created instances. Then EntityMetadataValidator calls
relation.getEntityValue(entityInstance) during initialization just to detect initialized arrays;
with the new getEntityValue implementation, accessing entity[this.propertyName] can invoke the
getter, triggering unexpected queries and returning a Promise (ignored/not awaited), which can lead
to performance regressions and potential unhandled rejections.

src/metadata/RelationMetadata.ts[468-494]
src/query-builder/RelationLoader.ts[409-477]
src/metadata/EntityMetadata.ts[564-595]
src/metadata-builder/EntityMetadataValidator.ts[200-220]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` now accesses `entity[this.propertyName]` for lazy relations even when `getLazyRelationsPromiseValue` is `false`. If `this.propertyName` is a lazy-loading getter (as installed by `RelationLoader.enableLazyLoad`), that access can trigger a DB query and/or return a Promise into code paths that expect a concrete value or `undefined`.
### Issue Context
- Lazy relations are implemented using `Object.defineProperty(..., { get() { ... relationLoader.load(...) } })`.
- Multiple call sites (e.g. metadata validation and persistence subject building) call `getEntityValue(entity)` with the default flag and do not expect to cause IO or to receive a Promise.
### Fix Focus Areas
- src/metadata/RelationMetadata.ts[475-493]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Null-proto getter check throws🐞 Bug ⛯ Reliability
Description
The new lazy-relation getter detection calls Object.getOwnPropertyDescriptor on
Object.getPrototypeOf(entity) without guarding against a null prototype, which can throw a TypeError
and break persistence/transform flows operating on plain objects.
Code

src/metadata/RelationMetadata.ts[R482-488]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
Evidence
RelationMetadata.getEntityValue now unconditionally passes Object.getPrototypeOf(entity) into
Object.getOwnPropertyDescriptor; if prototype is null, that call throws. TypeORM code explicitly
considers Object.create(null) objects as “plain objects”, and relation.getEntityValue is invoked on
user-provided plain objects during transformation/merge operations, making this a plausible runtime
crash.

src/metadata/RelationMetadata.ts[475-493]
src/util/OrmUtils.ts[538-545]
src/query-builder/transformer/PlainObjectToNewEntityTransformer.ts[56-64]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` calls `Object.getOwnPropertyDescriptor(Object.getPrototypeOf(entity), ...)` without checking if the prototype is `null`. For `Object.create(null)`-style objects, `Object.getPrototypeOf(entity)` returns `null`, and `Object.getOwnPropertyDescriptor(null, ...)` throws a `TypeError`.
## Issue Context
TypeORM utilities explicitly treat `Object.create(null)` objects as valid “plain objects”, and `relation.getEntityValue(...)` is called on user-provided plain objects in transformers.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[482-488]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (5)
4. getEntityValue triggers lazy load🐞 Bug ⛯ Reliability
Description
The new lazy-relation branch reads entity[this.propertyName] (including inside an `instanceof
Promise` check), which can invoke the lazy getter and start DB queries unexpectedly, and can also
return a Promise to callers that expect concrete relation values or undefined. This can cause
unexpected queries during initialization/validation and break persistence/transform flows when an
unresolved Promise is returned.
Code

src/metadata/RelationMetadata.ts[R482-491]

+                if (
+                    !Object.getOwnPropertyDescriptor(
+                        entity,
+                        this.propertyName,
+                    )?.get &&
+                    entity[this.propertyName] instanceof Promise
+                ) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
RelationLoader.enableLazyLoad defines a getter for lazy relations that calls
relationLoader.load(...) (DB query) when the property is accessed. EntityMetadata.create applies
this lazy getter to created instances. Then EntityMetadataValidator calls
relation.getEntityValue(entityInstance) during initialization just to detect initialized arrays;
with the new getEntityValue implementation, accessing entity[this.propertyName] can invoke the
getter, triggering unexpected queries and returning a Promise (ignored/not awaited), which can lead
to performance regressions and potential unhandled rejections.

src/metadata/RelationMetadata.ts[468-494]
src/query-builder/RelationLoader.ts[409-477]
src/metadata/EntityMetadata.ts[564-595]
src/metadata-builder/EntityMetadataValidator.ts[200-220]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` now accesses `entity[this.propertyName]` for lazy relations even when `getLazyRelationsPromiseValue` is `false`. If `this.propertyName` is a lazy-loading getter (as installed by `RelationLoader.enableLazyLoad`), that access can trigger a DB query and/or return a Promise into code paths that expect a concrete value or `undefined`.
### Issue Context
- Lazy relations are implemented using `Object.defineProperty(..., { get() { ... relationLoader.load(...) } })`.
- Multiple call sites (e.g. metadata validation and persistence subject building) call `getEntityValue(entity)` with the default flag and do not expect to cause IO or to receive a Promise.
### Fix Focus Areas
- src/metadata/RelationMetadata.ts[475-493]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Null-proto getter check throws🐞 Bug ⛯ Reliability
Description
The new lazy-relation getter detection calls Object.getOwnPropertyDescriptor on
Object.getPrototypeOf(entity) without guarding against a null prototype, which can throw a TypeError
and break persistence/transform flows operating on plain objects.
Code

src/metadata/RelationMetadata.ts[R482-488]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
Evidence
RelationMetadata.getEntityValue now unconditionally passes Object.getPrototypeOf(entity) into
Object.getOwnPropertyDescriptor; if prototype is null, that call throws. TypeORM code explicitly
considers Object.create(null) objects as “plain objects”, and relation.getEntityValue is invoked on
user-provided plain objects during transformation/merge operations, making this a plausible runtime
crash.

src/metadata/RelationMetadata.ts[475-493]
src/util/OrmUtils.ts[538-545]
src/query-builder/transformer/PlainObjectToNewEntityTransformer.ts[56-64]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` calls `Object.getOwnPropertyDescriptor(Object.getPrototypeOf(entity), ...)` without checking if the prototype is `null`. For `Object.create(null)`-style objects, `Object.getPrototypeOf(entity)` returns `null`, and `Object.getOwnPropertyDescriptor(null, ...)` throws a `TypeError`.
## Issue Context
TypeORM utilities explicitly treat `Object.create(null)` objects as valid “plain objects”, and `relation.getEntityValue(...)` is called on user-provided plain objects in transformers.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[482-488]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. getEntityValue triggers lazy load🐞 Bug ⛯ Reliability
Description
The new lazy-relation branch reads entity[this.propertyName] (including inside an `instanceof
Promise` check), which can invoke the lazy getter and start DB queries unexpectedly, and can also
return a Promise to callers that expect concrete relation values or undefined. This can cause
unexpected queries during initialization/validation and break persistence/transform flows when an
unresolved Promise is returned.
Code

src/metadata/RelationMetadata.ts[R482-491]

+                if (
+                    !Object.getOwnPropertyDescriptor(
+                        entity,
+                        this.propertyName,
+                    )?.get &&
+                    entity[this.propertyName] instanceof Promise
+                ) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
RelationLoader.enableLazyLoad defines a getter for lazy relations that calls
relationLoader.load(...) (DB query) when the property is accessed. EntityMetadata.create applies
this lazy getter to created instances. Then EntityMetadataValidator calls
relation.getEntityValue(entityInstance) during initialization just to detect initialized arrays;
with the new getEntityValue implementation, accessing entity[this.propertyName] can invoke the
getter, triggering unexpected queries and returning a Promise (ignored/not awaited), which can lead
to performance regressions and potential unhandled rejections.

src/metadata/RelationMetadata.ts[468-494]
src/query-builder/RelationLoader.ts[409-477]
src/metadata/EntityMetadata.ts[564-595]
src/metadata-builder/EntityMetadataValidator.ts[200-220]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` now accesses `entity[this.propertyName]` for lazy relations even when `getLazyRelationsPromiseValue` is `false`. If `this.propertyName` is a lazy-loading getter (as installed by `RelationLoader.enableLazyLoad`), that access can trigger a DB query and/or return a Promise into code paths that expect a concrete value or `undefined`.
### Issue Context
- Lazy relations are implemented using `Object.defineProperty(..., { get() { ... relationLoader.load(...) } })`.
- Multiple call sites (e.g. metadata validation and persistence subject building) call `getEntityValue(entity)` with the default flag and do not expect to cause IO or to receive a Promise.
### Fix Focus Areas
- src/metadata/RelationMetadata.ts[475-493]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Null-proto getter check throws🐞 Bug ⛯ Reliability
Description
The new lazy-relation getter detection calls Object.getOwnPropertyDescriptor on
Object.getPrototypeOf(entity) without guarding against a null prototype, which can throw a TypeError
and break persistence/transform flows operating on plain objects.
Code

src/metadata/RelationMetadata.ts[R482-488]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
Evidence
RelationMetadata.getEntityValue now unconditionally passes Object.getPrototypeOf(entity) into
Object.getOwnPropertyDescriptor; if prototype is null, that call throws. TypeORM code explicitly
considers Object.create(null) objects as “plain objects”, and relation.getEntityValue is invoked on
user-provided plain objects during transformation/merge operations, making this a plausible runtime
crash.

src/metadata/RelationMetadata.ts[475-493]
src/util/OrmUtils.ts[538-545]
src/query-builder/transformer/PlainObjectToNewEntityTransformer.ts[56-64]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` calls `Object.getOwnPropertyDescriptor(Object.getPrototypeOf(entity), ...)` without checking if the prototype is `null`. For `Object.create(null)`-style objects, `Object.getPrototypeOf(entity)` returns `null`, and `Object.getOwnPropertyDescriptor(null, ...)` throws a `TypeError`.
## Issue Context
TypeORM utilities explicitly treat `Object.create(null)` objects as valid “plain objects”, and `relation.getEntityValue(...)` is called on user-provided plain objects in transformers.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[482-488]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. getEntityValue triggers lazy load 🐞 Bug ⛯ Reliability
Description
The new lazy-relation branch reads entity[this.propertyName] (including inside an `instanceof
Promise` check), which can invoke the lazy getter and start DB queries unexpectedly, and can also
return a Promise to callers that expect concrete relation values or undefined. This can cause
unexpected queries during initialization/validation and break persistence/transform flows when an
unresolved Promise is returned.
Code

src/metadata/RelationMetadata.ts[R482-491]

+                if (
+                    !Object.getOwnPropertyDescriptor(
+                        entity,
+                        this.propertyName,
+                    )?.get &&
+                    entity[this.propertyName] instanceof Promise
+                ) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
RelationLoader.enableLazyLoad defines a getter for lazy relations that calls
relationLoader.load(...) (DB query) when the property is accessed. EntityMetadata.create applies
this lazy getter to created instances. Then EntityMetadataValidator calls
relation.getEntityValue(entityInstance) during initialization just to detect initialized arrays;
with the new getEntityValue implementation, accessing entity[this.propertyName] can invoke the
getter, triggering unexpected queries and returning a Promise (ignored/not awaited), which can lead
to performance regressions and potential unhandled rejections.

src/metadata/RelationMetadata.ts[468-494]
src/query-builder/RelationLoader.ts[409-477]
src/metadata/EntityMetadata.ts[564-595]
src/metadata-builder/EntityMetadataValidator.ts[200-220]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` now accesses `entity[this.propertyName]` for lazy relations even when `getLazyRelationsPromiseValue` is `false`. If `this.propertyName` is a lazy-loading getter (as installed by `RelationLoader.enableLazyLoad`), that access can trigger a DB query and/or return a Promise into code paths that expect a concrete value or `undefined`.
### Issue Context
- Lazy relations are implemented using `Object.defineProperty(..., { get() { ... relationLoader.load(...) } })`.
- Multiple call sites (e.g. metadata validation and persistence subject building) call `getEntityValue(entity)` with the default flag and do not expect to cause IO or to receive a Promise.
### Fix Focus Areas
- src/metadata/RelationMetadata.ts[475-493]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

9. @param tags lack descriptions 📘 Rule violation ✓ Correctness
Description
Several new JSDoc @param tags were added without any descriptive text, adding comment noise and
style inconsistency. This conflicts with the requirement to avoid AI-like/low-value commentary
additions.
Code

src/metadata/RelationMetadata.ts[R413-414]

+     * @param entity
+     * @param getLazyRelationsPromiseValue
Evidence
PR Compliance ID 4 requires avoiding AI-generated noise and inconsistent style; the PR adds multiple
bare @param lines that provide no additional information (only parameter names).

Rule 4: Remove AI-generated noise
src/metadata/RelationMetadata.ts[410-415]
src/metadata/RelationMetadata.ts[600-605]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several JSDoc blocks add `@param <name>` entries with no descriptive text. These add low-value commentary and can be considered AI-generated noise / style inconsistency.
## Issue Context
The compliance checklist requires avoiding AI-like slop and inconsistent style. If parameter documentation is desired, it should include meaningful descriptions; otherwise, these tags should be removed.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[369-372]
- src/metadata/RelationMetadata.ts[385-392]
- src/metadata/RelationMetadata.ts[410-415]
- src/metadata/RelationMetadata.ts[499-506]
- src/metadata/RelationMetadata.ts[549-552]
- src/metadata/RelationMetadata.ts[600-605]
- src/metadata/RelationMetadata.ts[609-614]
- src/metadata/RelationMetadata.ts[632-636]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Lazy relation fix undocumented 📘 Rule violation ✓ Correctness
Description
The PR changes runtime behavior for lazy one-to-many relation value retrieval, which can affect
application behavior. No documentation changes were included to reflect this user-facing behavior
fix.
Code

src/metadata/RelationMetadata.ts[R482-493]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
+
+                if (hasGetter || entity[this.propertyName] instanceof Promise) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
Compliance requires documentation updates for user-facing changes; the PR modifies lazy relation
handling logic but does not include any accompanying doc update in the provided changes, and the PR
checklist item for docs is left unchecked.

Rule 2: Docs updated for user-facing changes
src/metadata/RelationMetadata.ts[482-493]


11. Getter check incomplete🐞 Bug ⛯ Reliability
Description
Getter detection only checks the entity and its immediate prototype, so a getter higher in the
prototype chain can be missed and inadvertently executed when evaluating entity[propertyName]. This
can cause unexpected side effects (including lazy-load queries) in edge cases with non-trivial
prototype chains (e.g., inheritance/mixins).
Code

src/metadata/RelationMetadata.ts[R482-493]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
+
+                if (hasGetter || entity[this.propertyName] instanceof Promise) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
The implementation only inspects one prototype level for an accessor, then may read
entity[this.propertyName]. TypeORM supports inheritance trees, and lazy-load accessors are
installed via Object.defineProperty (including on prototypes), which makes deeper prototype chains
a realistic shape; walking the chain would make the getter-avoidance logic robust.

src/metadata/RelationMetadata.ts[482-493]
src/metadata-builder/EntityMetadataBuilder.ts[446-455]
src/query-builder/RelationLoader.ts[440-477]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue()` only checks for an accessor getter on the object itself and its immediate prototype. If a getter exists higher in the prototype chain, the method can still end up evaluating `entity[propertyName]`, invoking the getter and potentially triggering unexpected side effects.
### Issue Context
TypeORM supports inheritance trees and installs lazy relation accessors via `Object.defineProperty` (including on prototypes). A safer approach is to walk the prototype chain when checking for accessors.
### Fix Focus Areas
- src/metadata/RelationMetadata.ts[482-493]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (8)
12. @param tags lack descriptions 📘 Rule violation ✓ Correctness
Description
Several new JSDoc @param tags were added without any descriptive text, adding comment noise and
style inconsistency. This conflicts with the requirement to avoid AI-like/low-value commentary
additions.
Code

src/metadata/RelationMetadata.ts[R413-414]

+     * @param entity
+     * @param getLazyRelationsPromiseValue
Evidence
PR Compliance ID 4 requires avoiding AI-generated noise and inconsistent style; the PR adds multiple
bare @param lines that provide no additional information (only parameter names).

Rule 4: Remove AI-generated noise
src/metadata/RelationMetadata.ts[410-415]
src/metadata/RelationMetadata.ts[600-605]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several JSDoc blocks add `@param <name>` entries with no descriptive text. These add low-value commentary and can be considered AI-generated noise / style inconsistency.
## Issue Context
The compliance checklist requires avoiding AI-like slop and inconsistent style. If parameter documentation is desired, it should include meaningful descriptions; otherwise, these tags should be removed.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[369-372]
- src/metadata/RelationMetadata.ts[385-392]
- src/metadata/RelationMetadata.ts[410-415]
- src/metadata/RelationMetadata.ts[499-506]
- src/metadata/RelationMetadata.ts[549-552]
- src/metadata/RelationMetadata.ts[600-605]
- src/metadata/RelationMetadata.ts[609-614]
- src/metadata/RelationMetadata.ts[632-636]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


13. Getter scan misses shadowing 🐞 Bug ✓ Correctness
Description
The new prototype-chain scan treats the relation as “lazy/unassigned” if it finds any getter up the
chain, even when the entity defines its own data property that shadows that getter. In such cases
(e.g., entities/deserialized objects that define the property via defineProperty), an explicitly
assigned relation value can still be skipped during persistence.
Code

src/metadata/RelationMetadata.ts[R482-500]

+                let hasGetter = false
+                let proto: object | null = entity
+                while (proto) {
+                    if (
+                        Object.getOwnPropertyDescriptor(
+                            proto,
+                            this.propertyName,
+                        )?.get
+                    ) {
+                        hasGetter = true
+                        break
+                    }
+                    proto = Object.getPrototypeOf(proto)
+                }
+
+                if (hasGetter || entity[this.propertyName] instanceof Promise) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
RelationMetadata now walks the prototype chain and returns undefined whenever any getter is found,
without stopping when a shadowing own property descriptor exists. TypeORM installs lazy accessors on
the entity prototype, so the prototype chain commonly contains a getter for lazy relations; if a
consumer shadows it with a data property, this logic will still find the prototype getter and
incorrectly ignore the shadowing value.

src/metadata/RelationMetadata.ts[475-503]
src/metadata-builder/EntityMetadataBuilder.ts[397-409]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` scans the entire prototype chain for a getter and returns `undefined` if any getter exists, even if the relation name is shadowed by an own *data* property on the entity.
### Issue Context
This can cause explicitly assigned relation values to be ignored on entity-like objects that shadow the prototype accessor (e.g., via `Object.defineProperty` or certain deserialization patterns).
### Fix Focus Areas
- src/metadata/RelationMetadata.ts[482-500]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


14. Embedded lazy path unchanged 🐞 Bug ✓ Correctness
Description
The new assigned-value handling was only added for non-embedded relations; the embedded relation
branch still returns undefined for lazy relations unless ____ is populated. Since embeddeds can
contain relations, the same class of bug remains for embedded lazy relations.
Code

src/metadata/RelationMetadata.ts[R482-500]

+                let hasGetter = false
+                let proto: object | null = entity
+                while (proto) {
+                    if (
+                        Object.getOwnPropertyDescriptor(
+                            proto,
+                            this.propertyName,
+                        )?.get
+                    ) {
+                        hasGetter = true
+                        break
+                    }
+                    proto = Object.getPrototypeOf(proto)
+                }
+
+                if (hasGetter || entity[this.propertyName] instanceof Promise) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
RelationMetadata has a separate embeddedMetadata branch where lazy relations still always return
undefined unless the backing ____ exists or getLazyRelationsPromiseValue is true.
EmbeddedMetadata explicitly builds relationsFromTree, so relations-in-embedded is a
supported/considered feature and this branch is reachable.

src/metadata/RelationMetadata.ts[448-465]
src/metadata/EmbeddedMetadata.ts[308-314]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The embedded-metadata branch of `RelationMetadata.getEntityValue` did not receive the new assigned-value handling for lazy relations, leaving embedded lazy relations unable to surface assigned values unless `__<prop>__` is set.
### Issue Context
Embeddeds can contain relations (`relationsFromTree`), so this branch is reachable for relation metadata.
### Fix Focus Areas
- src/metadata/RelationMetadata.ts[454-465]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


15. Test doesn't hit new code 🐞 Bug ⛯ Reliability
Description
The added lazy test assigns Promise.resolve([...]), but save() yields await Promise.resolve()
before building subjects, allowing the lazy setter microtask to populate __posts__ so
getEntityValue returns early. This likely doesn’t exercise the newly added “no getter +
non-Promise value” code path, reducing confidence that the fix is validated.
Code

test/functional/persistence/one-to-many-lazy/persistence-one-to-many-lazy.test.ts[R77-85]

+                const fetchedUser = await entityManager.findOneOrFail(
+                    LazyUser,
+                    {
+                        where: { id: user.id },
+                    },
+                )
+                fetchedUser.posts = Promise.resolve([savedPost])
+                await entityManager.save(fetchedUser)
+
Evidence
RelationLoader.enableLazyLoad stores assigned Promises in __promise___ and transfers resolved
results into ____ in a .then(...) microtask. EntityPersistExecutor.execute begins by `await
Promise.resolve()` to flush microtasks before it proceeds to relation subject building, so by the
time persistence reads the relation, ____ is likely already set and the new getter/prototype
detection branch is bypassed.

test/functional/persistence/one-to-many-lazy/persistence-one-to-many-lazy.test.ts[77-85]
src/persistence/EntityPersistExecutor.ts[41-48]
src/query-builder/RelationLoader.ts[425-437]
src/query-builder/RelationLoader.ts[419-423]
src/metadata/RelationMetadata.ts[475-481]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new functional test likely passes without executing the newly added `hasGetter`/non-Promise-return path in `RelationMetadata.getEntityValue`, so it may not validate the intended fix.
### Issue Context
`EntityPersistExecutor` performs `await Promise.resolve()` before building relation subjects, which flushes microtasks that populate `__<prop>__` for `Promise.resolve(...)` assignments via the lazy setter.
### Fix Focus Areas
- test/functional/persistence/one-to-many-lazy/persistence-one-to-many-lazy.test.ts[59-96]
- src/persistence/EntityPersistExecutor.ts[46-48]
- src/query-builder/RelationLoader.ts[419-474]
- src/metadata/RelationMetadata.ts[475-500]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


16. @param tags lack descriptions 📘 Rule violation ✓ Correctness
Description
Several new JSDoc @param tags were added without any descriptive text, adding comment noise and
style inconsistency. This conflicts with the requirement to avoid AI-like/low-value commentary
additions.
Code

src/metadata/RelationMetadata.ts[R413-414]

+     * @param entity
+     * @param getLazyRelationsPromiseValue
Evidence
PR Compliance ID 4 requires avoiding AI-generated noise and inconsistent style; the PR adds multiple
bare @param lines that provide no additional information (only parameter names).

Rule 4: Remove AI-generated noise
src/metadata/RelationMetadata.ts[410-415]
src/metadata/RelationMetadata.ts[600-605]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several JSDoc blocks add `@param <name>` entries with no descriptive text. These add low-value commentary and can be considered AI-generated noise / style inconsistency.
## Issue Context
The compliance checklist requires avoiding AI-like slop and inconsistent style. If parameter documentation is desired, it should include meaningful descriptions; otherwise, these tags should be removed.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[369-372]
- src/metadata/RelationMetadata.ts[385-392]
- src/metadata/RelationMetadata.ts[410-415]
- src/metadata/RelationMetadata.ts[499-506]
- src/metadata/RelationMetadata.ts[549-552]
- src/metadata/RelationMetadata.ts[600-605]
- src/metadata/RelationMetadata.ts[609-614]
- src/metadata/RelationMetadata.ts[632-636]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


17. Lazy relation fix undocumented 📘 Rule violation ✓ Correctness
Description
The PR changes runtime behavior for lazy one-to-many relation value retrieval, which can affect
application behavior. No documentation changes were included to reflect this user-facing behavior
fix.
Code

src/metadata/RelationMetadata.ts[R482-493]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
+
+                if (hasGetter || entity[this.propertyName] instanceof Promise) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
Compliance requires documentation updates for user-facing changes; the PR modifies lazy relation
handling logic but does not include any accompanying doc update in the provided changes, and the PR
checklist item for docs is left unchecked.

Rule 2: Docs updated for user-facing changes
src/metadata/RelationMetadata.ts[482-493]


18. Getter check incomplete 🐞 Bug ⛯ Reliability
Description
Getter detection only checks the entity and its immediate prototype, so a getter higher in the
prototype chain can be missed and inadvertently executed when evaluating entity[propertyName]. This
can cause unexpected side effects (including lazy-load queries) in edge cases with non-trivial
prototype chains (e.g., inheritance/mixins).
Code

src/metadata/RelationMetadata.ts[R482-493]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
+
+                if (hasGetter || entity[this.propertyName] instanceof Promise) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
The implementation only inspects one prototype level for an accessor, then may read
entity[this.propertyName]. TypeORM supports inheritance trees, and lazy-load accessors are
installed via Object.defineProperty (including on prototypes), which makes deeper prototype chains
a realistic shape; walking the chain would make the getter-avoidance logic robust.

src/metadata/RelationMetadata.ts[482-493]
src/metadata-builder/EntityMetadataBuilder.ts[446-455]
src/query-builder/RelationLoader.ts[440-477]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue()` only checks for an accessor getter on the object itself and its immediate prototype. If a getter exists higher in the prototype chain, the method can still end up evaluating `entity[propertyName]`, invoking the getter and potentially triggering unexpected side effects.
### Issue Context
TypeORM supports inheritance trees and installs lazy relation accessors via `Object.defineProperty` (including on prototypes). A safer approach is to walk the prototype chain when checking for accessors.
### Fix Focus Areas
- src/metadata/RelationMetadata.ts[482-493]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


19. @param tags lack descriptions 📘 Rule violation ✓ Correctness
Description
Several new JSDoc @param tags were added without any descriptive text, adding comment noise and
style inconsistency. This conflicts with the requirement to avoid AI-like/low-value commentary
additions.
Code

src/metadata/RelationMetadata.ts[R413-414]

+     * @param entity
+     * @param getLazyRelationsPromiseValue
Evidence
PR Compliance ID 4 requires avoiding AI-generated noise and inconsistent style; the PR adds multiple
bare @param lines that provide no additional information (only parameter names).

Rule 4: Remove AI-generated noise
src/metadata/RelationMetadata.ts[410-415]
src/metadata/RelationMetadata.ts[600-605]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several JSDoc blocks add `@param <name>` entries with no descriptive text. These add low-value commentary and can be considered AI-generated noise / style inconsistency.
## Issue Context
The compliance checklist requires avoiding AI-like slop and inconsistent style. If parameter documentation is desired, it should include meaningful descriptions; otherwise, these tags should be removed.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[369-372]
- src/metadata/RelationMetadata.ts[385-392]
- src/metadata/RelationMetadata.ts[410-415]
- src/metadata/RelationMetadata.ts[499-506]
- src/metadata/RelationMetadata.ts[549-552]
- src/metadata/RelationMetadata.ts[600-605]
- src/metadata/RelationMetadata.ts[609-614]
- src/metadata/RelationMetadata.ts[632-636]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Null-proto getter check throws 🐞 Bug ⛯ Reliability
Description
The new lazy-relation getter detection calls Object.getOwnPropertyDescriptor on
Object.getPrototypeOf(entity) without guarding against a null prototype, which can throw a TypeError
and break persistence/transform flows operating on plain objects.
Code

src/metadata/RelationMetadata.ts[R482-488]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
Evidence
RelationMetadata.getEntityValue now unconditionally passes Object.getPrototypeOf(entity) into
Object.getOwnPropertyDescriptor; if prototype is null, that call throws. TypeORM code explicitly
considers Object.create(null) objects as “plain objects”, and relation.getEntityValue is invoked on
user-provided plain objects during transformation/merge operations, making this a plausible runtime
crash.

src/metadata/RelationMetadata.ts[475-493]
src/util/OrmUtils.ts[538-545]
src/query-builder/transformer/PlainObjectToNewEntityTransformer.ts[56-64]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` calls `Object.getOwnPropertyDescriptor(Object.getPrototypeOf(entity), ...)` without checking if the prototype is `null`. For `Object.create(null)`-style objects, `Object.getPrototypeOf(entity)` returns `null`, and `Object.getOwnPropertyDescriptor(null, ...)` throws a `TypeError`.
## Issue Context
TypeORM utilities explicitly treat `Object.create(null)` objects as valid “plain objects”, and `relation.getEntityValue(...)` is called on user-provided plain objects in transformers.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[482-488]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. getEntityValue triggers lazy load 🐞 Bug ⛯ Reliability
Description
The new lazy-relation branch reads entity[this.propertyName] (including inside an `instanceof
Promise` check), which can invoke the lazy getter and start DB queries unexpectedly, and can also
return a Promise to callers that expect concrete relation values or undefined. This can cause
unexpected queries during initialization/validation and break persistence/transform flows when an
unresolved Promise is returned.
Code

src/metadata/RelationMetadata.ts[R482-491]

+                if (
+                    !Object.getOwnPropertyDescriptor(
+                        entity,
+                        this.propertyName,
+                    )?.get &&
+                    entity[this.propertyName] instanceof Promise
+                ) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
RelationLoader.enableLazyLoad defines a getter for lazy relations that calls
relationLoader.load(...) (DB query) when the property is accessed. EntityMetadata.create applies
this lazy getter to created instances. Then EntityMetadataValidator calls
relation.getEntityValue(entityInstance) during initialization just to detect initialized arrays;
with the new getEntityValue implementation, accessing entity[this.propertyName] can invoke the
getter, triggering unexpected queries and returning a Promise (ignored/not awaited), which can lead
to performance regressions and potential unhandled rejections.

src/metadata/RelationMetadata.ts[468-494]
src/query-builder/RelationLoader.ts[409-477]
src/metadata/EntityMetadata.ts[564-595]
src/metadata-builder/EntityMetadataValidator.ts[200-220]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` now accesses `entity[this.propertyName]` for lazy relations even when `getLazyRelationsPromiseValue` is `false`. If `this.propertyName` is a lazy-loading getter (as installed by `RelationLoader.enableLazyLoad`), that access can trigger a DB query and/or return a Promise into code paths that expect a concrete value or `undefined`.
### Issue Context
- Lazy relations are implemented using `Object.defineProperty(..., { get() { ... relationLoader.load(...) } })`.
- Multiple call sites (e.g. metadata validation and persistence subject building) call `getEntityValue(entity)` with the default flag and do not expect to cause IO or to receive a Promise.
### Fix Focus Areas
- src/metadata/RelationMetadata.ts[475-493]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Lazy relation fix undocumented 📘 Rule violation ✓ Correctness ⭐ New
Description
The PR changes runtime behavior for lazy one-to-many relation value retrieval, which can affect
application behavior. No documentation changes were included to reflect this user-facing behavior
fix.
Code

src/metadata/RelationMetadata.ts[R482-493]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
+
+                if (hasGetter || entity[this.propertyName] instanceof Promise) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
Compliance requires documentation updates for user-facing changes; the PR modifies lazy relation
handling logic but does not include any accompanying doc update in the provided changes, and the PR
checklist item for docs is left unchecked.

Rule 2: Docs updated for user-facing changes
src/metadata/RelationMetadata.ts[482-493]


4. Getter check incomplete 🐞 Bug ⛯ Reliability ⭐ New
Description
Getter detection only checks the entity and its immediate prototype, so a getter higher in the
prototype chain can be missed and inadvertently executed when evaluating entity[propertyName]. This
can cause unexpected side effects (including lazy-load queries) in edge cases with non-trivial
prototype chains (e.g., inheritance/mixins).
Code

src/metadata/RelationMetadata.ts[R482-493]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
+
+                if (hasGetter || entity[this.propertyName] instanceof Promise) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
The implementation only inspects one prototype level for an accessor, then may read
entity[this.propertyName]. TypeORM supports inheritance trees, and lazy-load accessors are
installed via Object.defineProperty (including on prototypes), which makes deeper prototype chains
a realistic shape; walking the chain would make the getter-avoidance logic robust.

src/metadata/RelationMetadata.ts[482-493]
src/metadata-builder/EntityMetadataBuilder.ts[446-455]
src/query-builder/RelationLoader.ts[440-477]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`RelationMetadata.getEntityValue()` only checks for an accessor getter on the object itself and its immediate prototype. If a getter exists higher in the prototype chain, the method can still end up evaluating `entity[propertyName]`, invoking the getter and potentially triggering unexpected side effects.

### Issue Context
TypeORM supports inheritance trees and installs lazy relation accessors via `Object.defineProperty` (including on prototypes). A safer approach is to walk the prototype chain when checking for accessors.

### Fix Focus Areas
- src/metadata/RelationMetadata.ts[482-493]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. @param tags lack descriptions 📘 Rule violation ✓ Correctness
Description
Several new JSDoc @param tags were added without any descriptive text, adding comment noise and
style inconsistency. This conflicts with the requirement to avoid AI-like/low-value commentary
additions.
Code

src/metadata/RelationMetadata.ts[R413-414]

+     * @param entity
+     * @param getLazyRelationsPromiseValue
Evidence
PR Compliance ID 4 requires avoiding AI-generated noise and inconsistent style; the PR adds multiple
bare @param lines that provide no additional information (only parameter names).

Rule 4: Remove AI-generated noise
src/metadata/RelationMetadata.ts[410-415]
src/metadata/RelationMetadata.ts[600-605]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several JSDoc blocks add `@param <name>` entries with no descriptive text. These add low-value commentary and can be considered AI-generated noise / style inconsistency.
## Issue Context
The compliance checklist requires avoiding AI-like slop and inconsistent style. If parameter documentation is desired, it should include meaningful descriptions; otherwise, these tags should be removed.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[369-372]
- src/metadata/RelationMetadata.ts[385-392]
- src/metadata/RelationMetadata.ts[410-415]
- src/metadata/RelationMetadata.ts[499-506]
- src/metadata/RelationMetadata.ts[549-552]
- src/metadata/RelationMetadata.ts[600-605]
- src/metadata/RelationMetadata.ts[609-614]
- src/metadata/RelationMetadata.ts[632-636]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

Code Review by Qodo

🐞 Bugs (8) 📘 Rule violations (3) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Null-proto getter check throws 🐞 Bug ⛯ Reliability
Description
The new lazy-relation getter detection calls Object.getOwnPropertyDescriptor on
Object.getPrototypeOf(entity) without guarding against a null prototype, which can throw a TypeError
and break persistence/transform flows operating on plain objects.
Code

src/metadata/RelationMetadata.ts[R482-488]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
Evidence
RelationMetadata.getEntityValue now unconditionally passes Object.getPrototypeOf(entity) into
Object.getOwnPropertyDescriptor; if prototype is null, that call throws. TypeORM code explicitly
considers Object.create(null) objects as “plain objects”, and relation.getEntityValue is invoked on
user-provided plain objects during transformation/merge operations, making this a plausible runtime
crash.

src/metadata/RelationMetadata.ts[475-493]
src/util/OrmUtils.ts[538-545]
src/query-builder/transformer/PlainObjectToNewEntityTransformer.ts[56-64]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` calls `Object.getOwnPropertyDescriptor(Object.getPrototypeOf(entity), ...)` without checking if the prototype is `null`. For `Object.create(null)`-style objects, `Object.getPrototypeOf(entity)` returns `null`, and `Object.getOwnPropertyDescriptor(null, ...)` throws a `TypeError`.
## Issue Context
TypeORM utilities explicitly treat `Object.create(null)` objects as valid “plain objects”, and `relation.getEntityValue(...)` is called on user-provided plain objects in transformers.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[482-488]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. getEntityValue triggers lazy load 🐞 Bug ⛯ Reliability
Description
The new lazy-relation branch reads entity[this.propertyName] (including inside an `instanceof
Promise` check), which can invoke the lazy getter and start DB queries unexpectedly, and can also
return a Promise to callers that expect concrete relation values or undefined. This can cause
unexpected queries during initialization/validation and break persistence/transform flows when an
unresolved Promise is returned.
Code

src/metadata/RelationMetadata.ts[R482-491]

+                if (
+                    !Object.getOwnPropertyDescriptor(
+                        entity,
+                        this.propertyName,
+                    )?.get &&
+                    entity[this.propertyName] instanceof Promise
+                ) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
RelationLoader.enableLazyLoad defines a getter for lazy relations that calls
relationLoader.load(...) (DB query) when the property is accessed. EntityMetadata.create applies
this lazy getter to created instances. Then EntityMetadataValidator calls
relation.getEntityValue(entityInstance) during initialization just to detect initialized arrays;
with the new getEntityValue implementation, accessing entity[this.propertyName] can invoke the
getter, triggering unexpected queries and returning a Promise (ignored/not awaited), which can lead
to performance regressions and potential unhandled rejections.

src/metadata/RelationMetadata.ts[468-494]
src/query-builder/RelationLoader.ts[409-477]
src/metadata/EntityMetadata.ts[564-595]
src/metadata-builder/EntityMetadataValidator.ts[200-220]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` now accesses `entity[this.propertyName]` for lazy relations even when `getLazyRelationsPromiseValue` is `false`. If `this.propertyName` is a lazy-loading getter (as installed by `RelationLoader.enableLazyLoad`), that access can trigger a DB query and/or return a Promise into code paths that expect a concrete value or `undefined`.
### Issue Context
- Lazy relations are implemented using `Object.defineProperty(..., { get() { ... relationLoader.load(...) } })`.
- Multiple call sites (e.g. metadata validation and persistence subject building) call `getEntityValue(entity)` with the default flag and do not expect to cause IO or to receive a Promise.
### Fix Focus Areas
- src/metadata/RelationMetadata.ts[475-493]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Null-proto getter check throws 🐞 Bug ⛯ Reliability
Description
The new lazy-relation getter detection calls Object.getOwnPropertyDescriptor on
Object.getPrototypeOf(entity) without guarding against a null prototype, which can throw a TypeError
and break persistence/transform flows operating on plain objects.
Code

src/metadata/RelationMetadata.ts[R482-488]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
Evidence
RelationMetadata.getEntityValue now unconditionally passes Object.getPrototypeOf(entity) into
Object.getOwnPropertyDescriptor; if prototype is null, that call throws. TypeORM code explicitly
considers Object.create(null) objects as “plain objects”, and relation.getEntityValue is invoked on
user-provided plain objects during transformation/merge operations, making this a plausible runtime
crash.

src/metadata/RelationMetadata.ts[475-493]
src/util/OrmUtils.ts[538-545]
src/query-builder/transformer/PlainObjectToNewEntityTransformer.ts[56-64]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` calls `Object.getOwnPropertyDescriptor(Object.getPrototypeOf(entity), ...)` without checking if the prototype is `null`. For `Object.create(null)`-style objects, `Object.getPrototypeOf(entity)` returns `null`, and `Object.getOwnPropertyDescriptor(null, ...)` throws a `TypeError`.
## Issue Context
TypeORM utilities explicitly treat `Object.create(null)` objects as valid “plain objects”, and `relation.getEntityValue(...)` is called on user-provided plain objects in transformers.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[482-488]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. getEntityValue triggers lazy load 🐞 Bug ⛯ Reliability
Description
The new lazy-relation branch reads entity[this.propertyName] (including inside an `instanceof
Promise` check), which can invoke the lazy getter and start DB queries unexpectedly, and can also
return a Promise to callers that expect concrete relation values or undefined. This can cause
unexpected queries during initialization/validation and break persistence/transform flows when an
unresolved Promise is returned.
Code

src/metadata/RelationMetadata.ts[R482-491]

+                if (
+                    !Object.getOwnPropertyDescriptor(
+                        entity,
+                        this.propertyName,
+                    )?.get &&
+                    entity[this.propertyName] instanceof Promise
+                ) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
RelationLoader.enableLazyLoad defines a getter for lazy relations that calls
relationLoader.load(...) (DB query) when the property is accessed. EntityMetadata.create applies
this lazy getter to created instances. Then EntityMetadataValidator calls
relation.getEntityValue(entityInstance) during initialization just to detect initialized arrays;
with the new getEntityValue implementation, accessing entity[this.propertyName] can invoke the
getter, triggering unexpected queries and returning a Promise (ignored/not awaited), which can lead
to performance regressions and potential unhandled rejections.

src/metadata/RelationMetadata.ts[468-494]
src/query-builder/RelationLoader.ts[409-477]
src/metadata/EntityMetadata.ts[564-595]
src/metadata-builder/EntityMetadataValidator.ts[200-220]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue` now accesses `entity[this.propertyName]` for lazy relations even when `getLazyRelationsPromiseValue` is `false`. If `this.propertyName` is a lazy-loading getter (as installed by `RelationLoader.enableLazyLoad`), that access can trigger a DB query and/or return a Promise into code paths that expect a concrete value or `undefined`.
### Issue Context
- Lazy relations are implemented using `Object.defineProperty(..., { get() { ... relationLoader.load(...) } })`.
- Multiple call sites (e.g. metadata validation and persistence subject building) call `getEntityValue(entity)` with the default flag and do not expect to cause IO or to receive a Promise.
### Fix Focus Areas
- src/metadata/RelationMetadata.ts[475-493]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Getter scan misses shadowing 🐞 Bug ✓ Correctness ⭐ New
Description
The new prototype-chain scan treats the relation as “lazy/unassigned” if it finds any getter up the
chain, even when the entity defines its own data property that shadows that getter. In such cases
(e.g., entities/deserialized objects that define the property via defineProperty), an explicitly
assigned relation value can still be skipped during persistence.
Code

src/metadata/RelationMetadata.ts[R482-500]

+                let hasGetter = false
+                let proto: object | null = entity
+                while (proto) {
+                    if (
+                        Object.getOwnPropertyDescriptor(
+                            proto,
+                            this.propertyName,
+                        )?.get
+                    ) {
+                        hasGetter = true
+                        break
+                    }
+                    proto = Object.getPrototypeOf(proto)
+                }
+
+                if (hasGetter || entity[this.propertyName] instanceof Promise) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
RelationMetadata now walks the prototype chain and returns undefined whenever any getter is found,
without stopping when a shadowing own property descriptor exists. TypeORM installs lazy accessors on
the entity prototype, so the prototype chain commonly contains a getter for lazy relations; if a
consumer shadows it with a data property, this logic will still find the prototype getter and
incorrectly ignore the shadowing value.

src/metadata/RelationMetadata.ts[475-503]
src/metadata-builder/EntityMetadataBuilder.ts[397-409]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`RelationMetadata.getEntityValue` scans the entire prototype chain for a getter and returns `undefined` if any getter exists, even if the relation name is shadowed by an own *data* property on the entity.

### Issue Context
This can cause explicitly assigned relation values to be ignored on entity-like objects that shadow the prototype accessor (e.g., via `Object.defineProperty` or certain deserialization patterns).

### Fix Focus Areas
- src/metadata/RelationMetadata.ts[482-500]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Embedded lazy path unchanged 🐞 Bug ✓ Correctness ⭐ New
Description
The new assigned-value handling was only added for non-embedded relations; the embedded relation
branch still returns undefined for lazy relations unless __<prop>__ is populated. Since embeddeds
can contain relations, the same class of bug remains for embedded lazy relations.
Code

src/metadata/RelationMetadata.ts[R482-500]

+                let hasGetter = false
+                let proto: object | null = entity
+                while (proto) {
+                    if (
+                        Object.getOwnPropertyDescriptor(
+                            proto,
+                            this.propertyName,
+                        )?.get
+                    ) {
+                        hasGetter = true
+                        break
+                    }
+                    proto = Object.getPrototypeOf(proto)
+                }
+
+                if (hasGetter || entity[this.propertyName] instanceof Promise) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
RelationMetadata has a separate embeddedMetadata branch where lazy relations still always return
undefined unless the backing __<prop>__ exists or getLazyRelationsPromiseValue is true.
EmbeddedMetadata explicitly builds relationsFromTree, so relations-in-embedded is a
supported/considered feature and this branch is reachable.

src/metadata/RelationMetadata.ts[448-465]
src/metadata/EmbeddedMetadata.ts[308-314]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The embedded-metadata branch of `RelationMetadata.getEntityValue` did not receive the new assigned-value handling for lazy relations, leaving embedded lazy relations unable to surface assigned values unless `__&lt;prop&gt;__` is set.

### Issue Context
Embeddeds can contain relations (`relationsFromTree`), so this branch is reachable for relation metadata.

### Fix Focus Areas
- src/metadata/RelationMetadata.ts[454-465]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Test doesn't hit new code 🐞 Bug ⛯ Reliability ⭐ New
Description
The added lazy test assigns Promise.resolve([...]), but save() yields await Promise.resolve()
before building subjects, allowing the lazy setter microtask to populate __posts__ so
getEntityValue returns early. This likely doesn’t exercise the newly added “no getter +
non-Promise value” code path, reducing confidence that the fix is validated.
Code

test/functional/persistence/one-to-many-lazy/persistence-one-to-many-lazy.test.ts[R77-85]

+                const fetchedUser = await entityManager.findOneOrFail(
+                    LazyUser,
+                    {
+                        where: { id: user.id },
+                    },
+                )
+                fetchedUser.posts = Promise.resolve([savedPost])
+                await entityManager.save(fetchedUser)
+
Evidence
RelationLoader.enableLazyLoad stores assigned Promises in __promise_<prop>__ and transfers
resolved results into __<prop>__ in a .then(...) microtask. EntityPersistExecutor.execute
begins by await Promise.resolve() to flush microtasks before it proceeds to relation subject
building, so by the time persistence reads the relation, __<prop>__ is likely already set and the
new getter/prototype detection branch is bypassed.

test/functional/persistence/one-to-many-lazy/persistence-one-to-many-lazy.test.ts[77-85]
src/persistence/EntityPersistExecutor.ts[41-48]
src/query-builder/RelationLoader.ts[425-437]
src/query-builder/RelationLoader.ts[419-423]
src/metadata/RelationMetadata.ts[475-481]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The new functional test likely passes without executing the newly added `hasGetter`/non-Promise-return path in `RelationMetadata.getEntityValue`, so it may not validate the intended fix.

### Issue Context
`EntityPersistExecutor` performs `await Promise.resolve()` before building relation subjects, which flushes microtasks that populate `__&lt;prop&gt;__` for `Promise.resolve(...)` assignments via the lazy setter.

### Fix Focus Areas
- test/functional/persistence/one-to-many-lazy/persistence-one-to-many-lazy.test.ts[59-96]
- src/persistence/EntityPersistExecutor.ts[46-48]
- src/query-builder/RelationLoader.ts[419-474]
- src/metadata/RelationMetadata.ts[475-500]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (4)
8. @param tags lack descriptions 📘 Rule violation ✓ Correctness
Description
Several new JSDoc @param tags were added without any descriptive text, adding comment noise and
style inconsistency. This conflicts with the requirement to avoid AI-like/low-value commentary
additions.
Code

src/metadata/RelationMetadata.ts[R413-414]

+     * @param entity
+     * @param getLazyRelationsPromiseValue
Evidence
PR Compliance ID 4 requires avoiding AI-generated noise and inconsistent style; the PR adds multiple
bare @param lines that provide no additional information (only parameter names).

Rule 4: Remove AI-generated noise
src/metadata/RelationMetadata.ts[410-415]
src/metadata/RelationMetadata.ts[600-605]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several JSDoc blocks add `@param &amp;lt;name&amp;gt;` entries with no descriptive text. These add low-value commentary and can be considered AI-generated noise / style inconsistency.
## Issue Context
The compliance checklist requires avoiding AI-like slop and inconsistent style. If parameter documentation is desired, it should include meaningful descriptions; otherwise, these tags should be removed.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[369-372]
- src/metadata/RelationMetadata.ts[385-392]
- src/metadata/RelationMetadata.ts[410-415]
- src/metadata/RelationMetadata.ts[499-506]
- src/metadata/RelationMetadata.ts[549-552]
- src/metadata/RelationMetadata.ts[600-605]
- src/metadata/RelationMetadata.ts[609-614]
- src/metadata/RelationMetadata.ts[632-636]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Lazy relation fix undocumented 📘 Rule violation ✓ Correctness
Description
The PR changes runtime behavior for lazy one-to-many relation value retrieval, which can affect
application behavior. No documentation changes were included to reflect this user-facing behavior
fix.
Code

src/metadata/RelationMetadata.ts[R482-493]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
+
+                if (hasGetter || entity[this.propertyName] instanceof Promise) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
Compliance requires documentation updates for user-facing changes; the PR modifies lazy relation
handling logic but does not include any accompanying doc update in the provided changes, and the PR
checklist item for docs is left unchecked.

Rule 2: Docs updated for user-facing changes
src/metadata/RelationMetadata.ts[482-493]


10. Getter check incomplete 🐞 Bug ⛯ Reliability
Description
Getter detection only checks the entity and its immediate prototype, so a getter higher in the
prototype chain can be missed and inadvertently executed when evaluating entity[propertyName]. This
can cause unexpected side effects (including lazy-load queries) in edge cases with non-trivial
prototype chains (e.g., inheritance/mixins).
Code

src/metadata/RelationMetadata.ts[R482-493]

+                const hasGetter =
+                    Object.getOwnPropertyDescriptor(entity, this.propertyName)
+                        ?.get ||
+                    Object.getOwnPropertyDescriptor(
+                        Object.getPrototypeOf(entity),
+                        this.propertyName,
+                    )?.get
+
+                if (hasGetter || entity[this.propertyName] instanceof Promise) {
+                    return undefined
+                }
+                return entity[this.propertyName]
Evidence
The implementation only inspects one prototype level for an accessor, then may read
entity[this.propertyName]. TypeORM supports inheritance trees, and lazy-load accessors are
installed via Object.defineProperty (including on prototypes), which makes deeper prototype chains
a realistic shape; walking the chain would make the getter-avoidance logic robust.

src/metadata/RelationMetadata.ts[482-493]
src/metadata-builder/EntityMetadataBuilder.ts[446-455]
src/query-builder/RelationLoader.ts[440-477]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RelationMetadata.getEntityValue()` only checks for an accessor getter on the object itself and its immediate prototype. If a getter exists higher in the prototype chain, the method can still end up evaluating `entity[propertyName]`, invoking the getter and potentially triggering unexpected side effects.
### Issue Context
TypeORM supports inheritance trees and installs lazy relation accessors via `Object.defineProperty` (including on prototypes). A safer approach is to walk the prototype chain when checking for accessors.
### Fix Focus Areas
- src/metadata/RelationMetadata.ts[482-493]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. @param tags lack descriptions 📘 Rule violation ✓ Correctness
Description
Several new JSDoc @param tags were added without any descriptive text, adding comment noise and
style inconsistency. This conflicts with the requirement to avoid AI-like/low-value commentary
additions.
Code

src/metadata/RelationMetadata.ts[R413-414]

+     * @param entity
+     * @param getLazyRelationsPromiseValue
Evidence
PR Compliance ID 4 requires avoiding AI-generated noise and inconsistent style; the PR adds multiple
bare @param lines that provide no additional information (only parameter names).

Rule 4: Remove AI-generated noise
src/metadata/RelationMetadata.ts[410-415]
src/metadata/RelationMetadata.ts[600-605]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several JSDoc blocks add `@param &amp;amp;lt;name&amp;amp;gt;` entries with no descriptive text. These add low-value commentary and can be considered AI-generated noise / style inconsistency.
## Issue Context
The compliance checklist requires avoiding AI-like slop and inconsistent style. If parameter documentation is desired, it should include meaningful descriptions; otherwise, these tags should be removed.
## Fix Focus Areas
- src/metadata/RelationMetadata.ts[369-372]
- src/metadata/RelationMetadata.ts[385-392]
- src/metadata/RelationMetadata.ts[410-415]
- src/metadata/RelationMetadata.ts[499-506]
- src/metadata/RelationMetadata.ts[549-552]
- src/metadata/RelationMetadata.ts[600-605]
- src/metadata/RelationMetadata.ts[609-614]
- src/metadata/RelationMetadata.ts[632-636]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cannot save a one-to-many relation

3 participants