{"record":{"id":"c6ba336568177528","repo":"agalwood/Motrix","slug":"taskfinalizemetamissing","errorCode":"TaskFinalizeMetaMissing","errorMessage":"Torrent metadata path not set","messagePattern":"Torrent metadata path not set","errorType":"exception","errorClass":"AppError","httpStatus":null,"severity":"error","filePath":"src/core/task/actions/finalize-task.ts","lineNumber":782,"sourceCode":"  task: DownloadTask,\n  previousStatus: TaskStatus,\n  deps: FinalizeTaskDeps,\n  occurredAt = Date.now(),\n  accuracy: TaskActivityAccuracy = TaskActivityAccuracy.Exact\n): Promise<void> {\n  await recordTaskTransitionOrWarn(task, previousStatus, deps, {\n    occurredAt,\n    accuracy,\n    failureMessage: FINALIZE_RECORD_FAILURE_MESSAGE,\n  })\n}\n\nasync function readTorrentMeta(\n  task: DownloadTask,\n  deps: FinalizeTaskDeps\n): Promise<Uint8Array> {\n  if (!task.torrentMetaPath) {\n    throw new AppError(\n      ErrorCode.TaskFinalizeMetaMissing,\n      'Torrent metadata path not set'\n    )\n  }\n  try {\n    return await deps.torrentMetaStore.read(task.torrentMetaPath)\n  } catch (e) {\n    throw new AppError(\n      ErrorCode.TaskFinalizeMetaMissing,\n      `Torrent metadata is missing at ${task.torrentMetaPath}`,\n      e\n    )\n  }\n}\n\nasync function persistDesiredFinalPath(\n  task: DownloadTask,\n  desiredFinalPath: string,","sourceCodeStart":764,"sourceCodeEnd":800,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/task/actions/finalize-task.ts#L764-L800","documentation":"Thrown by `readTorrentMeta` (finalize-task.ts:782) when a torrent-like task reaches finalize/seeding but `task.torrentMetaPath` is null/empty. Seeding requires the original `.torrent` bytes, and without a persisted sidecar path the reseed dance cannot run. This is a state/data defect — the task row is inconsistent.","triggerScenarios":"A BT or Magnet task was created through a path that skipped `torrentMetaStore.persist` (no torrentBytes supplied); a Magnet task whose metadata was resolved in-engine but never written to a sidecar; corrupted/migrated DB row where torrentMetaPath column was dropped.","commonSituations":"Migration between versions changed how torrentMetaPath is populated; a Magnet task moved directly to Finalizing without the metadata-resolution instance recording the path; manual DB edits or a partial create that failed after the row insert but before persist.","solutions":["Audit the create path that produced this task — confirm torrentBytes were supplied and `torrentMetaStore.persist` ran successfully.","If this is a Magnet task, ensure the metadata-resolution instance persists torrentMetaPath before allowing transition out of MetadataReady.","For affected rows, mark the task as non-seedable (or complete without seeding) instead of crashing finalize.","Add a precondition check at finalize entry that rejects seedable BT tasks lacking torrentMetaPath with a clearer error."],"exampleFix":"// before\nif (!task.torrentMetaPath) {\n  throw new AppError(ErrorCode.TaskFinalizeMetaMissing, 'Torrent metadata path not set')\n}\n\n// after — degrade gracefully for HTTP-like leak, hard-fail only for seedable BT\nif (!task.torrentMetaPath && task.kind === 'bt') {\n  await failFinalize(task, deps, {\n    errorMessage: 'Cannot seed: torrent metadata path not set',\n    errorDetailKey: 'task.error.detail.metaPathMissing',\n    hookCode: 'TASK_FINALIZE_META_MISSING',\n  })\n  throw new AppError(ErrorCode.TaskFinalizeMetaMissing, 'Torrent metadata path not set')\n}","handlingStrategy":"validation","validationCode":"function hasSeedableMetadata(task) {\n  // Only BT-like tasks need torrentMetaPath for seeding\n  if (task.kind !== 'bt') return true\n  return Boolean(task.torrentMetaPath)\n}\nif (!hasSeedableMetadata(task)) {\n  // mark non-seedable / skip seeding instead of throwing in finalize\n}","typeGuard":"function isMetaPathMissing(e) { return e instanceof AppError && e.code === ErrorCode.TaskFinalizeMetaMissing && /not set/.test(e.message) }","tryCatchPattern":"if (!task.torrentMetaPath) {\n  // degrade: complete without seeding rather than crash finalize\n  await completeWithoutSeeding(task, deps)\n  return\n}","preventionTips":["Persist torrentMetaPath during create for every BT task before it becomes finalize-eligible.","Run a data-integrity sweep on migrated rows to backfill missing torrentMetaPath.","Add a finalize precondition that rejects seedable BT tasks without a path early.","Audit create paths to ensure torrentBytes were supplied and persisted."],"tags":["bittorrent","finalize","state","metadata","data-integrity"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}