Fix Card.copy() mutating source card and normalize customFields

Card.copy() mutated `this` directly (boardId, labelIds, customFields,
etc.), corrupting the cached source card object and causing intermittent
failures on repeated copy operations. Now works on a shallow copy.

Also normalizes customFields to [] when it's not an array (e.g. legacy
{} data in the database), preventing "Custom fields must be an array"
schema validation errors on both copy and move operations.
This commit is contained in:
Harry Adel 2026-02-05 02:28:15 +02:00
parent d68ad47de6
commit e8b9a3a163

View file

@ -611,6 +611,15 @@ Cards.helpers({
const oldId = this._id;
const oldCard = ReactiveCache.getCard(oldId);
// Work on a shallow copy to avoid mutating the source card in ReactiveCache
const cardData = { ...this };
delete cardData._id;
// Normalize customFields to ensure it's always an array
if (!Array.isArray(cardData.customFields)) {
cardData.customFields = [];
}
// we must only copy the labels and custom fields if the target board
// differs from the source board
if (this.boardId !== boardId) {
@ -633,19 +642,16 @@ Cards.helpers({
}),
'_id',
);
// now set the new label ids
delete this.labelIds;
this.labelIds = newCardLabels;
cardData.labelIds = newCardLabels;
this.customFields = await this.mapCustomFieldsToBoard(newBoard._id);
cardData.customFields = await this.mapCustomFieldsToBoard(newBoard._id);
}
delete this._id;
this.boardId = boardId;
this.cardNumber = ReactiveCache.getBoard(boardId).getNextCardNumber();
this.swimlaneId = swimlaneId;
this.listId = listId;
const _id = Cards.insert(this);
cardData.boardId = boardId;
cardData.cardNumber = ReactiveCache.getBoard(boardId).getNextCardNumber();
cardData.swimlaneId = swimlaneId;
cardData.listId = listId;
const _id = Cards.insert(cardData);
// Copy attachments
oldCard.attachments()
@ -669,8 +675,6 @@ Cards.helpers({
ReactiveCache.getCardComments({ cardId: oldId }).forEach(cmt => {
cmt.copy(_id);
});
// restore the id, otherwise new copies will fail
this._id = oldId;
return _id;
},
@ -2102,6 +2106,11 @@ Cards.helpers({
});
mutatedFields.customFields = await this.mapCustomFieldsToBoard(newBoard._id);
// Ensure customFields is always an array (guards against legacy {} data)
if (!Array.isArray(mutatedFields.customFields)) {
mutatedFields.customFields = [];
}
}
await Cards.updateAsync(this._id, { $set: mutatedFields });