Description
Removing an item from a many() relation array using .splice() inside an update() callback throws RangeError: Maximum call stack size exceeded. This happens when the related entity itself has a self-referential many() relation defined on it (in the reproduction, users has a followedBy: many(users) relation), even though the crash occurs on an unrelated collection (articles).
Reproduction
StackBlitz: https://stackblitz.com/edit/vitejs-vite-l3gsewlc?file=src%2Fmain.ts&terminal=dev
import { Collection } from '@msw/data';
import * as z from 'zod';
const userSchema = z.object({
id: z.number(),
get followedBy() {
return z.array(userSchema);
},
});
const articleSchema = z.object({
id: z.number(),
get favouritedBy() {
return z.array(userSchema);
},
});
const users = new Collection({ schema: userSchema });
const articles = new Collection({ schema: articleSchema });
users.defineRelations(({ many }) => ({
followedBy: many(users),
}));
articles.defineRelations(({ many }) => ({
favouritedBy: many(users),
}));
// john and jane both follow each other
const johndoe = await users.create({ id: 1, followedBy: [] });
const janesmith = await users.create({ id: 2, followedBy: [] });
await users.update(johndoe, {
data(user) {
user.followedBy.push(janesmith);
},
});
await users.update(janesmith, {
data(user) {
user.followedBy.push(johndoe);
},
});
const article = await articles.create({ id: 1, favouritedBy: [janesmith] });
// Uncaught RangeError: Maximum call stack size exceeded
await articles.update(article, {
data(article) {
article.favouritedBy.splice(0, 1);
},
});
Expected behavior
janesmith should be removed from article.favouritedBy without error.
Actual behavior
Throws:
RangeError: Maximum call stack size exceeded
at Array.filter (<anonymous>)
at Query.<anonymous> (collection-CpEWi2De.mjs:102:22)
at Query.test (collection-CpEWi2De.mjs:66:17)
at #query (collection-CpEWi2De.mjs:765:32)
at #query.next (<anonymous>)
at Array.from (<anonymous>)
at Collection.findMany (collection-CpEWi2De.mjs:548:25)
at collection-CpEWi2De.mjs:416:29
at Array.flatMap (<anonymous>)
at Many.resolve (collection-CpEWi2De.mjs:415:34)
This suggests an infinite recursion between Many.resolve → Collection.findMany → Query.test, likely triggered by the self-referential followedBy relation on users being re-resolved every time favouritedBy is queried/mutated, with no cycle guard.
Additional notes
- Removing the self-referential
followedBy: many(users) relation (or not populating it with mutual references) avoids the crash.
- The crash happens on
articles.update, a collection that doesn't itself have a circular relation — the cycle appears to come from resolving the related users records.
Description
Removing an item from a
many()relation array using.splice()inside anupdate()callback throwsRangeError: Maximum call stack size exceeded. This happens when the related entity itself has a self-referentialmany()relation defined on it (in the reproduction,usershas afollowedBy: many(users)relation), even though the crash occurs on an unrelated collection (articles).Reproduction
StackBlitz: https://stackblitz.com/edit/vitejs-vite-l3gsewlc?file=src%2Fmain.ts&terminal=dev
Expected behavior
janesmithshould be removed fromarticle.favouritedBywithout error.Actual behavior
Throws:
This suggests an infinite recursion between
Many.resolve→Collection.findMany→Query.test, likely triggered by the self-referentialfollowedByrelation onusersbeing re-resolved every timefavouritedByis queried/mutated, with no cycle guard.Additional notes
followedBy: many(users)relation (or not populating it with mutual references) avoids the crash.articles.update, a collection that doesn't itself have a circular relation — the cycle appears to come from resolving the relatedusersrecords.