{"record":{"id":"b331a6a991595f9c","repo":"agalwood/Motrix","slug":"tasknotfound-b331a6","errorCode":"TaskNotFound","errorMessage":"task ${taskId} not found","messagePattern":"task (.+?) not found","errorType":"exception","errorClass":"AppError","httpStatus":null,"severity":"warning","filePath":"src/core/torrent/magnet-tracker.ts","lineNumber":741,"sourceCode":"    entry.hiddenTombstone = true\n  }\n\n  /** Re-emit Events.MagnetFileSelection for a task whose metadata already\n   *  resolved (aggStatus=MetadataReady) but whose file-selection dialog was\n   *  dismissed — so the user can re-open it without re-adding the magnet.\n   *  Cache-first (the entry survives onComplete); falls back to the\n   *  persisted metadataDir or durable task.torrentMetaPath after a restart\n   *  (primeFromDatabase skips MetadataReady rows, so there is no cache entry\n   *  then). A no-op\n   *  when the task is not MetadataReady (the UI button gates on this state;\n   *  this guards a stale click). Throws MagnetResolveFailed when the saved\n   *  .torrent is no longer on disk (e.g. an OS reboot cleared the temp dir).\n   *  Routing/window handling is identical to the first emit: the bootstrap\n   *  forwards MagnetFileSelection to the add-task window. */\n  async reopenFileSelection(taskId: string): Promise<void> {\n    const pair = this.db.getTask(taskId)\n    if (!pair) {\n      throw new AppError(ErrorCode.TaskNotFound, `task ${taskId} not found`)\n    }\n    if (pair.task.aggStatus !== TaskStatus.MetadataReady) {\n      log.warn(\n        { taskId, status: pair.task.aggStatus },\n        'reopenFileSelection ignored: task not in MetadataReady'\n      )\n      return\n    }\n\n    const metaInst = pair.instances.find(\n      (i) => i.phase === TaskInstancePhase.MagnetMetadataResolution\n    )\n    const cached = this.findCacheEntryByTaskId(taskId)\n    const metadataDir =\n      cached?.metadataDir ??\n      (typeof metaInst?.payload.metadataDir === 'string'\n        ? metaInst.payload.metadataDir\n        : '')","sourceCodeStart":723,"sourceCodeEnd":759,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/torrent/magnet-tracker.ts#L723-L759","documentation":"Thrown by `reopenFileSelection` (magnet-tracker.ts:741) when `db.getTask(taskId)` returns null. The task the caller asked to re-open the magnet file-selection window for does not exist in the database. This guards a stale UI action against a task that was deleted or never created.","triggerScenarios":"A stale UI 'select files' click arrives after the task was removed; an IPC call with a wrong/old taskId; a task was cleared by clear-stopped-tasks between the window open and the click; the taskId was fabricated/typoed.","commonSituations":"Race between user clicking 'select files' and another flow deleting the task; stale renderer state after a clear-all; cross-window IPC with an outdated id; test/dev harness passing a bogus id.","solutions":["Have the renderer verify the task still exists (and is MetadataReady) before issuing reopenFileSelection; disable the action on deletion events.","Subscribe to task-deleted events in the renderer and dismiss the selection window.","Treat TaskNotFound as a no-op in the caller (log and close the window) rather than surfacing a hard error to the user.","Double-check the taskId source for typos or stale caches."],"exampleFix":"// before\nconst pair = this.db.getTask(taskId)\nif (!pair) {\n  throw new AppError(ErrorCode.TaskNotFound, `task ${taskId} not found`)\n}\n\n// after — degrade to a no-op for stale UI clicks\nconst pair = this.db.getTask(taskId)\nif (!pair) {\n  log.warn({ taskId }, 'reopenFileSelection ignored: task no longer exists')\n  return\n}","handlingStrategy":"validation","validationCode":"function taskStillExists(db, taskId) { return Boolean(db.getTask(taskId)) }","typeGuard":"function isTaskNotFound(e) { return e instanceof AppError && e.code === ErrorCode.TaskNotFound }","tryCatchPattern":"const pair = this.db.getTask(taskId)\nif (!pair) {\n  log.warn({ taskId }, 'reopenFileSelection ignored: task no longer exists')\n  return // benign no-op for stale UI clicks\n}","preventionTips":["Disable the 'select files' action in the renderer when the task is deleted.","Subscribe to task-deleted events and dismiss the selection window.","Treat TaskNotFound here as a no-op rather than surfacing a hard error.","Re-check existence immediately before issuing the IPC."],"tags":["magnet","task-lifecycle","state","ui-race"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}