agalwood/Motrix · error · AppError

TaskFinalizeMetaMissing

TaskFinalizeMetaMissing

Error message

Torrent metadata path not set

What it means

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.

Source

Thrown at src/core/task/actions/finalize-task.ts:782

  task: DownloadTask,
  previousStatus: TaskStatus,
  deps: FinalizeTaskDeps,
  occurredAt = Date.now(),
  accuracy: TaskActivityAccuracy = TaskActivityAccuracy.Exact
): Promise<void> {
  await recordTaskTransitionOrWarn(task, previousStatus, deps, {
    occurredAt,
    accuracy,
    failureMessage: FINALIZE_RECORD_FAILURE_MESSAGE,
  })
}

async function readTorrentMeta(
  task: DownloadTask,
  deps: FinalizeTaskDeps
): Promise<Uint8Array> {
  if (!task.torrentMetaPath) {
    throw new AppError(
      ErrorCode.TaskFinalizeMetaMissing,
      'Torrent metadata path not set'
    )
  }
  try {
    return await deps.torrentMetaStore.read(task.torrentMetaPath)
  } catch (e) {
    throw new AppError(
      ErrorCode.TaskFinalizeMetaMissing,
      `Torrent metadata is missing at ${task.torrentMetaPath}`,
      e
    )
  }
}

async function persistDesiredFinalPath(
  task: DownloadTask,
  desiredFinalPath: string,

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Audit the create path that produced this task — confirm torrentBytes were supplied and `torrentMetaStore.persist` ran successfully.
  2. If this is a Magnet task, ensure the metadata-resolution instance persists torrentMetaPath before allowing transition out of MetadataReady.
  3. For affected rows, mark the task as non-seedable (or complete without seeding) instead of crashing finalize.
  4. Add a precondition check at finalize entry that rejects seedable BT tasks lacking torrentMetaPath with a clearer error.

Example fix

// before
if (!task.torrentMetaPath) {
  throw new AppError(ErrorCode.TaskFinalizeMetaMissing, 'Torrent metadata path not set')
}

// after — degrade gracefully for HTTP-like leak, hard-fail only for seedable BT
if (!task.torrentMetaPath && task.kind === 'bt') {
  await failFinalize(task, deps, {
    errorMessage: 'Cannot seed: torrent metadata path not set',
    errorDetailKey: 'task.error.detail.metaPathMissing',
    hookCode: 'TASK_FINALIZE_META_MISSING',
  })
  throw new AppError(ErrorCode.TaskFinalizeMetaMissing, 'Torrent metadata path not set')
}
Defensive patterns

Strategy: validation

Validate before calling

function hasSeedableMetadata(task) {
  // Only BT-like tasks need torrentMetaPath for seeding
  if (task.kind !== 'bt') return true
  return Boolean(task.torrentMetaPath)
}
if (!hasSeedableMetadata(task)) {
  // mark non-seedable / skip seeding instead of throwing in finalize
}

Type guard

function isMetaPathMissing(e) { return e instanceof AppError && e.code === ErrorCode.TaskFinalizeMetaMissing && /not set/.test(e.message) }

Try / catch

if (!task.torrentMetaPath) {
  // degrade: complete without seeding rather than crash finalize
  await completeWithoutSeeding(task, deps)
  return
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12). Data as JSON: /api/errors/c6ba336568177528. Report an issue: GitHub.