agalwood/Motrix · error · AppError
TaskFinalizeMetaMissing
TaskFinalizeMetaMissing
Error message
Torrent metadata missing for task ${task.id} What it means
Thrown by `readBtMetadata` in re-add-task.ts:75 during the re-seed/retry path when `task.torrentMetaPath` is falsy. Re-adding a torrent-like task into aria2 requires the original `.torrent` bytes; without the persisted sidecar path the re-add cannot reconstruct engine inputs.
Source
Thrown at src/core/task/actions/re-add-task.ts:75
// not-found idempotence) is authoritative absence. forceRemove may have
// raced a row that was already stopped, so its failure alone must not
// retain a ghost owner.
return true
} catch (err) {
log.error(
{ err: String(err), engineTaskId },
'reAddTask: failed to remove replacement result after add failure'
)
return false
}
}
async function readBtMetadata(
task: DownloadTask,
deps: ReAddTaskDeps
): Promise<Uint8Array> {
if (!task.torrentMetaPath) {
throw new AppError(
ErrorCode.TaskFinalizeMetaMissing,
`Torrent metadata missing for task ${task.id}`
)
}
return deps.torrentMetaStore.read(task.torrentMetaPath)
}
/**
* Where aria2 should write this re-add.
*
* A Completed reseed points at `finalPath`: finalize already renamed the
* container away from `.motrix`, and that is where the seedable content
* lives (`diskPath` has been normalized to the same value anyway).
*
* A retry of a failed or removed download is the opposite case — finalize
* never ran, so the partial content is still in the in-flight `.motrix`
* container that `createTaskHandler` passed as `dir` at add time. Pointing
* `checkIntegrity` at `finalPath` there would scan an empty directory andView on GitHub (pinned to 1a708ee577)
Solutions
- Block retry in the UI when `canRebuildTaskInputs(task)` / `canReseed(task)` return false — surface the task as non-retryable instead of throwing.
- If the original `.torrent` is recoverable elsewhere, persist it via `torrentMetaStore.persist(taskId, bytes)` then retry.
- For migrated rows missing torrentMetaPath, mark them non-retryable and re-create from the source link instead.
- Ensure all BT create paths persist torrentMetaPath before the task becomes retryable.
Example fix
// before
async function readBtMetadata(task, deps) {
if (!task.torrentMetaPath) {
throw new AppError(ErrorCode.TaskFinalizeMetaMissing, `Torrent metadata missing for task ${task.id}`)
}
return deps.torrentMetaStore.read(task.torrentMetaPath)
}
// after — caller guards before invoking
if (!canReseed(task)) {
deps.log.warn({ taskId: task.id }, 'reAddTask: skipping reseed, no torrentMetaPath')
return false
} Defensive patterns
Strategy: validation
Validate before calling
// Guard at the re-add entry before calling readBtMetadata
import { canReseed } from '@core/task/shared'
if (!canReseed(task)) {
deps.log.warn({ taskId: task.id }, 'reAddTask: skipping reseed, torrentMetaPath missing')
return false
} Type guard
function isReAddMetaMissing(e) { return e instanceof AppError && e.code === ErrorCode.TaskFinalizeMetaMissing && /Torrent metadata missing for task/.test(e.message) } Try / catch
if (!task.torrentMetaPath) {
// mark non-retryable instead of throwing
deps.log.warn({ taskId: task.id }, 'reAddTask: no torrentMetaPath')
return false
} Prevention
- Gate the retry UI on canReseed(task) so the throw is never reached.
- Persist torrentMetaPath during create for all BT tasks.
- Backfill torrentMetaPath for migrated rows where possible.
- Surface non-retryable tasks clearly rather than letting users hit the throw.
When it happens
Trigger: User clicks retry on a stopped BT task that was created without persisted torrent bytes; a task imported via migration where torrentMetaPath was not backfilled; a re-add invoked on a task that originally only had an info-hash with no .torrent file.
Common situations: Tasks created in an older version before torrentMetaPath was mandatory; magnet tasks that never reached MetadataReady before being stopped; DB rows altered by maintenance scripts.
Related errors
- TaskFinalizeMetaMissing
- TaskNotRetryable
- TaskCreateTorrentMetaFailed
- MagnetResolveFailed
- plugin.metadata.value_not_serializable
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/f9aaaac883e9b932.
Report an issue: GitHub.