alibaba/canal · error · RuntimeException
download failed , url:{} , statusCode:{}
Error message
download failed , url:{} , statusCode:{} What it means
Thrown as RuntimeException from the RDS binlog download path when the HTTP GET to the RDS binlog download link returns a non-200 status code. The exception includes the download URL and the HTTP status code. This is the per-file download failure — the RDS API provided a pre-signed download URL, but fetching the actual binlog file content failed.
Source
Thrown at parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/rds/BinlogDownloadQueue.java:203
.register("https", new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE))
.build()))
.build();
} else {
httpClient = HttpClientBuilder.create().setMaxConnPerRoute(50).setMaxConnTotal(100).build();
}
try {
HttpGet httpGet = new HttpGet(downloadLink);
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(TIMEOUT)
.setConnectionRequestTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build();
httpGet.setConfig(requestConfig);
HttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpResponseStatus.OK.code()) {
throw new RuntimeException("download failed , url:" + downloadLink + " , statusCode:" + statusCode);
}
saveFile(new File(destDir), "mysql-bin." + fileName, response);
} finally {
httpClient.close();
}
}
private static void saveFile(File parentFile, String fileName, HttpResponse response) throws IOException {
InputStream is = response.getEntity().getContent();
boolean isChunked = response.getEntity().isChunked();
Header contentLengthHeader = response.getFirstHeader("Content-Length");
long totalSize = (isChunked || contentLengthHeader == null) ? 0 : Long.parseLong(contentLengthHeader.getValue());
if (response.getFirstHeader("Content-Disposition") != null) {
fileName = response.getFirstHeader("Content-Disposition").getValue();
fileName = StringUtils.substringAfter(fileName, "filename=");
}
boolean isTar = StringUtils.endsWith(fileName, ".tar");
FileUtils.forceMkdir(parentFile);View on GitHub (pinned to 87be50e876)
Solutions
- Check the status code: 403 = expired URL (re-request from RDS API), 404 = file gone, 429 = rate limit, 5xx = server error.
- Increase the TIMEOUT constant in BinlogDownloadQueue if downloads are timing out on large binlog files.
- Implement retry logic with exponential backoff around the download call for transient 5xx/429 errors.
- If 403 persists, verify the AccessKey/SecretKey are valid and not expired.
Example fix
// before — single attempt, no retry
BinlogFile binlogFile = downloadQueue.tryOne();
// after — retry with backoff for transient failures
int maxRetries = 3;
for (int i = 0; i < maxRetries; i++) {
try {
BinlogFile binlogFile = downloadQueue.tryOne();
break;
} catch (RuntimeException e) {
if (i == maxRetries - 1) throw e;
Thread.sleep((long) Math.pow(2, i) * 1000);
}
} Defensive patterns
Strategy: retry
Try / catch
int maxRetries = 3;
for (int attempt = 0; attempt < maxRetries; attempt++) {
try {
BinlogFile file = downloadQueue.tryOne();
break;
} catch (RuntimeException e) {
if (e.getMessage().contains("download failed")) {
int statusCode = extractStatusCode(e.getMessage());
if (statusCode == 403) {
// URL expired — re-request from RDS API
refreshDownloadLinks();
} else if (statusCode >= 500 && attempt < maxRetries - 1) {
Thread.sleep((long) Math.pow(2, attempt) * 1000);
continue;
}
}
throw e;
}
} Prevention
- Pre-signed RDS download URLs expire quickly — download promptly after obtaining them.
- Increase the TIMEOUT constant for large binlog files that take longer to download.
- Implement exponential backoff for 5xx and 429 HTTP responses.
When it happens
Trigger: httpClient.execute(httpGet) returns an HttpResponse whose status code is not 200 (HttpResponseStatus.OK.code()). Common non-200 codes: 403 (expired or invalid pre-signed URL), 404 (binlog file removed), 408/504 (timeout), 429 (rate limited), 500/502/503 (RDS storage backend error).
Common situations: The pre-signed download URL expired before the download started (RDS URLs have a short TTL). RDS rate-limited the download request. The RDS storage backend had a transient error. Network issues between Canal and the RDS OSS/storage endpoint. The binlog file was purged between the API listing and the actual download.
Related errors
- download binlog is null
- requestGet remote error, url={}, code={}, error msg={}
- requestGet remote error, request : {}
- requestPost remote error, request : {}, statusCode={};{}
- requestPost remote error, request : {}
AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14).
Data as JSON: /api/errors/f5cc689729b32545.
Report an issue: GitHub.