In this episode we'll learn to read the evidence when a download fails: enabling logging with --log and --log-level, understanding the log line format, mapping HTTP, TLS, FTP, and BitTorrent errors to their root causes, and opening the ports that DHT and BitTorrent peers need.

In episode 18 you made aria2 run unattended — and that's exactly the moment debugging skills matter most. A download that fails on an automation machine is no longer visible on a screen; it only leaves traces in the log and exit code. This episode 19 equips you with the habit of reading evidence: enabling logs, understanding their lines, then mapping the most common errors — HTTP, TLS, FTP, and BitTorrent — to their root causes and solutions.
The same principle applies here: don't guess, look at the log.
aria2 is silent by default when running normally. For debugging, enable logging to a file:
aria2c --log=/var/log/aria2/aria2.log --log-level=debug \
https://cdn.example.com/update.isoThe log level determines how much detail is recorded:
debug — the most detail: connections, HTTP requests, BitTorrent peers, everything is recorded.info — enough for daily operations: downloads started, finished, and major errors.notice — only important events, suitable for production.The level doesn't change whether errors exist, only how much detail they get. Use info in production and raise to debug only when something needs investigating — debug logs can be enormous and quickly fill a disk.
Tip
The most effective pattern: debug with --log=/tmp/aria2.log --log-level=debug, then read the results with tail -f /tmp/aria2.log in a separate terminal. "Record first, investigate later" beats guessing from on-screen error messages.
In the terminal, aria2 shows a console readout — a self-updating progress line. For scripts and daemons, that output is useless and just floods the log. Disable it with --show-console-readout=false, and set a periodic summary:
aria2c --show-console-readout=false --summary-interval=60 \
--log=/var/log/aria2/aria2.log --log-level=info \
--input-file=urls.txtWith this combination the terminal stays calm while every event is neatly recorded in the log. When it becomes a daemon (episode 20), these settings are what make the service log human-readable.
Every log line has three parts: timestamp, level, and message:
2026-08-03 10:11:12.345 NOTICE - Downloading item: 1
2026-08-03 10:11:14.221 WARN - Could not connect to 203.0.113.10:443
2026-08-03 10:11:15.077 ERROR - SSL certificate problem: self-signed certificate
2026-08-03 10:11:20.512 NOTICE - Download complete: /srv/downloads/update.isoThe key to reading it: look for ERROR and WARN levels first. A WARN line followed by an ERROR usually tells one story: the connection failed, then the protocol gave up. Read from the lowest level to the highest, and don't forget to match it against the exit code from episode 18.
The message Could not connect means the TCP connection to the server never formed. The causes are layered: server down, wrong port, or a firewall dropping packets. The way to tell them apart is simple — test the connection at a lower layer with another tool:
curl -v --connect-timeout 5 https://cdn.example.com/update.iso -o /dev/nullIf curl fails the same way, the problem is on the network or server, not in aria2. If curl succeeds, compare the URL you gave aria2 — a wrong scheme (http vs https) or wrong port is the most common and most easily missed cause.
TLS verification is enabled by default in aria2. When the server's certificate has a problem — self-signed, expired, or a host name mismatch — you'll see SSL certificate problem. The tempting shortcut is --check-certificate=false, but that disables server identity verification entirely.
Warning
Don't disable --check-certificate for servers handling important data — it opens the door to man-in-the-middle attacks. The right solution: use --ca-certificate to point at the correct CA bundle, or register the internal certificate in the system trust store. Verification stays on; you only control its source of trust.
For internal endpoints that genuinely use their own certificates, narrow the solution instead of disabling verification:
aria2c --ca-certificate=/etc/ssl/certs/ca-certificates.crt \
https://intranet.example.com/update.isoFTP has two layers of errors: authentication and file access. 530 Login incorrect means wrong credentials; 550 File not found means the path doesn't exist on the server. Active FTP (the default) often fails behind NAT because the server tries to contact you back — the solution is passive mode:
aria2c --ftp-pasv=true --ftp-user=arman --ftp-passwd=RAHASIA \
ftp://files.example.com/distros/update.isoNever put passwords on the command line for routine work — store them in netrc or a configuration file with tight permissions, as discussed in the download security episode.
Handshake failed in BitTorrent means the TCP connection to the peer succeeded, but the initial identity exchange failed — usually because the protocol versions don't match or the peer rejects an unrecognized client. This isn't always a total failure: with many peers available, aria2 will try another. If nearly all peers fail, check encryption:
aria2c --bt-min-crypto=plain --bt-require-crypto=false \
https://example.com/file.torrentThis is the most common reason torrents or magnets "run but never finish". BitTorrent needs TCP and UDP ports reachable from outside — by default the range 6881 to 6999. If a firewall or NAT blocks them, aria2 can only talk to peers that reach you, not the other way around. On an Ubuntu server:
sudo ufw allow 6881:6999/tcp
sudo ufw allow 6881:6999/udpFor magnet links that can't find any source at all, DHT is the key — make sure --enable-dht=true and the DHT UDP port aren't blocked. Don't forget dht.dat from the BitTorrent episode: that file is your saved knowledge of peers, so it must persist between sessions for DHT to "remember" where to look.
When a download misbehaves, follow a consistent ladder:
WARN and ERROR lines, read from the lowest level.curl to separate server problems from aria2 problems.This sequence keeps you from wasting time: if DNS fails, don't open the firewall. If SSL fails, don't fiddle with BitTorrent options. Each layer points to the next.
Episode 19 equipped you with a complete diagnostic kit: enabling logging with --log and --log-level, quieting the console readout for scripts and daemons, understanding the log line format, mapping HTTP, TLS, FTP, and BitTorrent errors to their root causes, and opening ports 6881 to 6999 for peers and DHT.
The most valuable thing isn't the tools — it's the habit: debugging is reading evidence in sequence, not guessing. Start from the exit code, descend to the log, isolate the layer, then check the environment.
In the next episode, episode 20, we raise everything to production level: production-ready setup — daemon, systemd, log rotation, Docker, and NAS. See you then!