lingochamp/FileDownloader · error · IllegalArgumentException

the download runnable must not be null!

Error message

the download runnable must not be null!

What it means

An IllegalArgumentException thrown in DownloadLaunchRunnable.fetchWithMultipleConnection when one of the per-range DownloadRunnable instances built for a multi-connection download is null. This is an internal invariant check: every slice of a multi-connection download must have a runnable before it is added to downloadRunnableList. Seeing it usually means the connection model list or runnable creation produced a null entry, typically due to corrupted breakpoint models.

Solutions

  1. Clear the download's breakpoint data (FileDownloader.getImpl().clear(taskId, path)) and restart the download from scratch
  2. Ensure the target file path and task are removed from the FileDownloader DB before resuming (delete the file and re-create the task)
  3. Check for concurrent downloads of the same ID/path from multiple processes and serialize them (use FileDownloadActivity/Service or a global lock)
  4. Upgrade to the latest FileDownloader version, which hardens breakpoint model consistency
  5. If reproducible, file a bug with the DB contents for the task ID

Example fix

// before: blindly resuming a possibly-corrupt task
FileDownloader.getImpl().create(url).setPath(path).start();
// after: clear stale breakpoint state when resuming fails
FileDownloader.getImpl().clear(taskId, path);
new File(path + ".temp").delete();
FileDownloader.getImpl().create(url).setPath(path).start();
Defensive patterns

Strategy: validation

Validate before calling

// Before resuming, ensure breakpoint state is consistent
FileDownloadTask task = FileDownloader.getImpl().create(url).setPath(path);
File temp = new File(path + ".temp");
if (!temp.exists()) {
    FileDownloader.getImpl().clear(taskId, path); // drop stale breakpoint rows
}

Try / catch

try {
    FileDownloader.getImpl().create(url).setPath(path).start(listener);
} catch (IllegalArgumentException e) {
    FileDownloader.getImpl().clear(taskId, path);
    new File(path + ".temp").delete();
    FileDownloader.getImpl().create(url).setPath(path).start(listener); // fresh start
}

Prevention

When it happens

Trigger: Resuming a download with multiple connections where the stored breakpoint/connection models (ConnectionModel from the database) are inconsistent or partially deleted, so a range chunk gets no runnable assigned before fetchWithMultipleConnection iterates the chunks.

Common situations: Database corruption or a version upgrade of FileDownloader leaving stale/invalid breakpoint records; resuming a task whose .temp file or breakpoint DB row was deleted mid-flight; concurrent access to the same download ID from multiple processes.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of lingochamp/FileDownloader@6237a8cac1 (2026-09-08). Data as JSON: /api/errors/296e06db8503038c. Report an issue: GitHub.

Appendix: source

Thrown at library/src/main/java/com/liulishuo/filedownloader/download/DownloadLaunchRunnable.java:726

            final DownloadRunnable runnable = builder
                    .setId(id)
                    .setConnectionIndex(connectionModel.getIndex())
                    .setCallback(this)
                    .setUrl(url)
                    .setEtag(withEtag ? etag : null)
                    .setHeader(userRequestHeader)
                    .setWifiRequired(isWifiRequired)
                    .setConnectionModel(connectionProfile)
                    .setPath(path)
                    .build();

            if (FileDownloadLog.NEED_LOG) {
                FileDownloadLog.d(this, "enable multiple connection: %s", connectionModel);
            }

            if (runnable == null) {
                throw new IllegalArgumentException("the download runnable must not be null!");
            }

            downloadRunnableList.add(runnable);
        }

        if (totalOffset != model.getSoFar()) {
            FileDownloadLog.w(this, "correct the sofar[%d] from connection table[%d]",
                    model.getSoFar(), totalOffset);
            model.setSoFar(totalOffset);
        }

        List<Callable<Object>> subTasks = new ArrayList<>(downloadRunnableList.size());
        for (DownloadRunnable runnable : downloadRunnableList) {
            if (paused) {
                runnable.pause();
                continue;
            }
            subTasks.add(Executors.callable(runnable));

View on GitHub (pinned to 6237a8cac1)