agalwood/Motrix · error · AppError
TaskNotRetryable
TaskNotRetryable
Error message
Task ${taskId} cannot be retried: required inputs are unavailable What it means
Thrown by re-add-task.ts:301 when `canRetry(task)` is true but `canRebuildTaskInputs(task)` is false. The task is formally in a retryable status, yet the engine dispatch cannot be reconstructed from the persisted record — e.g. a media task with no single re-addable handle, or a torrent-like task whose sidecar was dropped. The check rejects up front rather than failing mid-flow.
Source
Thrown at src/core/task/actions/re-add-task.ts:301
*/
async function reAddTaskUnderMutation(
taskId: string,
deps: ReAddTaskDeps
): Promise<void> {
const task = getTaskOrWarn(deps, taskId, 'reAddTask')
if (!task) return
if (canRetry(task)) {
// Retry additionally requires the engine dispatch to be reconstructable
// from the task record (canReseed already implies this for its own path,
// since it requires torrentMetaPath itself) — reject up front rather than
// discovering mid-flow (e.g. a media task with no single re-addable
// handle, or a torrent-like task with no persisted sidecar).
if (!canRebuildTaskInputs(task)) {
deps.log.warn(
{ taskId, status: task.status, type: task.type, kind: task.kind },
'reAddTask: task inputs cannot be rebuilt'
)
throw new AppError(
ErrorCode.TaskNotRetryable,
`Task ${taskId} cannot be retried: required inputs are unavailable`
)
}
} else if (!canReseed(task)) {
deps.log.warn(
{ taskId, status: task.status, type: task.type },
'reAddTask: task is not in a re-addable state'
)
return
}
// Resolve local prerequisites before touching the old engine GID or
// installing a durable reservation. From the reservation onward, the only
// fallible operation before publication is the engine dispatch itself.
const torrentMetadata = await readBtMetadata(task, deps)
let opts: EngineTaskOptions | null = null
try {
opts = await deps.adapter.getEngineTaskOptions(task.engineTaskId)View on GitHub (pinned to 1a708ee577)
Solutions
- Gate the retry affordance on `canRebuildTaskInputs(task)` in the UI/controller so the button is disabled, not the throw hit.
- For torrent-like tasks, restore torrentMetaPath (re-persist the .torrent) before retry.
- For media/non-retryable kinds, surface a clear 'cannot be retried' status instead of attempting re-add.
- If the task genuinely should be retryable, fix the create path so its inputs are fully persisted.
Example fix
// before
if (canRetry(task)) {
if (!canRebuildTaskInputs(task)) {
throw new AppError(ErrorCode.TaskNotRetryable, `Task ${taskId} cannot be retried: required inputs are unavailable`)
}
}
// after — UI layer disables the action upfront
const retryable = canRetry(task) && canRebuildTaskInputs(task)
if (!retryable) {
ui.setRetryEnabled(taskId, false, 'Required inputs are no longer available')
return
} Defensive patterns
Strategy: validation
Validate before calling
import { canRetry, canRebuildTaskInputs } from '@core/task/shared'
function isRetryable(task) {
return canRetry(task) && canRebuildTaskInputs(task)
}
if (!isRetryable(task)) {
ui.setRetryEnabled(task.id, false, 'Required inputs unavailable')
} Type guard
function isTaskNotRetryable(e) { return e instanceof AppError && e.code === ErrorCode.TaskNotRetryable } Try / catch
if (canRetry(task) && !canRebuildTaskInputs(task)) {
deps.log.warn({ taskId }, 'reAddTask: not retryable, inputs missing')
return { retried: false, reason: 'inputs_unavailable' }
} Prevention
- Disable the retry affordance unless canRebuildTaskInputs(task) is true.
- Ensure all create paths persist the inputs needed to reconstruct the engine dispatch.
- For media/non-retryable kinds, mark them non-retryable at create time.
- Restore torrentMetaPath for torrent-like tasks before allowing retry.
When it happens
Trigger: Retry invoked on a media task that has no resumable single handle; a torrent-like task where the persisted sidecar/path is missing; a task type whose kind never has rebuildable inputs.
Common situations: UI retry button enabled purely on status without the capability check; partial create left a row that looks retryable; tasks of a kind that the engine cannot re-dispatch (live stream, unsupported protocol).
Related errors
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/7a731b0f3d385399.
Report an issue: GitHub.