{"record":{"id":"a5cb3e61cc41c340","repo":"wekan/wekan","slug":"card-not-found-a5cb3e","errorCode":"card-not-found","errorMessage":"Card not found","messagePattern":"Card not found","errorType":"error_code","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"server/methods/positionHistory.js","lineNumber":71,"sourceCode":"      throw new Meteor.Error('not-authorized', 'You do not have access to this board.');\n    }\n\n    return list.trackOriginalPosition();\n  },\n\n  /**\n   * Track original position for a card\n   */\n  async 'positionHistory.trackCard'(cardId) {\n    check(cardId, String);\n\n    if (!this.userId) {\n      throw new Meteor.Error('not-authorized', 'You must be logged in.');\n    }\n\n    const card = await Cards.findOneAsync(cardId);\n    if (!card) {\n      throw new Meteor.Error('card-not-found', 'Card not found');\n    }\n\n    const board = await ReactiveCache.getBoard(card.boardId);\n    if (!board || !board.isVisibleBy({ _id: this.userId })) {\n      throw new Meteor.Error('not-authorized', 'You do not have access to this board.');\n    }\n\n    return card.trackOriginalPosition();\n  },\n\n  /**\n   * Get original position for a swimlane\n   */\n  async 'positionHistory.getSwimlaneOriginalPosition'(swimlaneId) {\n    check(swimlaneId, String);\n\n    if (!this.userId) {\n      throw new Meteor.Error('not-authorized', 'You must be logged in.');","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/wekan/wekan/blob/eb1433158b1804bcf3edaa5cb18f08ae2e04d6c2/server/methods/positionHistory.js#L53-L89","documentation":"The 'positionHistory.trackCard' method fetches the card with Cards.findOneAsync(cardId) after the login check. When no card matches, it throws 'card-not-found' with message 'Card not found'. It indicates the supplied cardId does not correspond to any document in the Cards collection.","triggerScenarios":"Meteor.call('positionHistory.trackCard', cardId) with an id of a card that was archived-and-deleted, a mis-keyed id (list or swimlane id passed in), or an id fabricated in a test/script against a different database.","commonSituations":"Optimistic UI dropping a card whose server document was deleted by another user; stale search results or links pointing at removed cards; copy-pasted ids between environments.","solutions":["Confirm the card id exists: Cards.findOne(cardId) on a subscribed client before calling.","Re-subscribe to the board to refresh deleted/archived card state.","Fix the caller passing the wrong id type.","Catch 'card-not-found' and skip tracking, cleaning up the stale client reference."],"exampleFix":"// before\nMeteor.call('positionHistory.trackCard', droppedCardId);\n\n// after\nif (Cards.findOne(droppedCardId)) {\n  Meteor.call('positionHistory.trackCard', droppedCardId);\n} else {\n  removeStaleReference(droppedCardId);\n}","handlingStrategy":"validation","validationCode":"if (!Cards.findOne(cardId)) { cleanupStaleCard(cardId); return; }\nMeteor.call('positionHistory.trackCard', cardId);","typeGuard":"function isExistingCard(cardId) {\n  return typeof cardId === 'string' && !!Cards.findOne(cardId);\n}","tryCatchPattern":"try {\n  await Meteor.callAsync('positionHistory.trackCard', cardId);\n} catch (e) {\n  if (e && e.error === 'card-not-found') {\n    cleanupStaleCard(cardId); // deleted elsewhere, drop reference\n  } else {\n    throw e;\n  }\n}","preventionTips":["Derive card ids from the reactive subscription, not from long-lived component state.","Listen for card-removal publications and purge local references immediately.","Validate id kind in drag payloads (card vs list vs swimlane).","Skip tracking for cards already flagged archived/deleted locally."],"tags":["meteor","not-found","card","position-history"],"backgroundTag":"document-not-found","analyzedSha":"eb1433158b1804bcf3edaa5cb18f08ae2e04d6c2","analyzedAt":"2026-09-01T21:05:02.951Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T01:17:15.007Z"}