theonedev/onedev · error · ExplicitException

Error downloading attachment (url: %s, error message: %s)

Error message

Error downloading attachment (url: %s, error message: %s)

What it means

Thrown when downloading a YouTrack attachment: JerseyUtils.checkStatus found the HTTP response was not successful, so the importer wraps the endpoint URL and the API error message in an ExplicitException and aborts the attachment download. The attachment content is never saved to OneDev in that case.

Source

Thrown at server-plugin/server-plugin-import-youtrack/src/main/java/io/onedev/server/plugin/imports/youtrack/ImportServer.java:403

					for (JsonNode attachmentNode : attachmentNodes) {
						String attachmentName = attachmentNode.get("name").asText(null);
						String attachmentUrl = attachmentNode.get("url").asText(null);
						long attachmentSize = attachmentNode.get("size").asLong(0);
						if (attachmentSize != 0 && attachmentName != null && attachmentUrl != null) {
							if (attachmentSize > maxUploadFileSize) {
								tooLargeAttachments.add(readableIssueId + ":" + attachmentName);
							} else {
								if (!attachmentUrl.startsWith("/api"))
									throw new ExplicitException("Unexpected attachment url: " + attachmentUrl);
								attachmentUrl = attachmentUrl.substring("/api".length());

								String endpoint = getApiEndpoint(attachmentUrl);
								WebTarget target = client.target(endpoint);
								Invocation.Builder builder = target.request();
								try (Response response = builder.get()) {
									String errorMessage = JerseyUtils.checkStatus(endpoint, response);
									if (errorMessage != null) {
										throw new ExplicitException(String.format(
												"Error downloading attachment (url: %s, error message: %s)",
												endpoint, errorMessage));
									}
									try (InputStream is = response.readEntity(InputStream.class)) {
										AttachmentService attachmentService = OneDev.getInstance(AttachmentService.class);
										String oneDevAttachmentName = attachmentService.saveAttachment(
												oneDevProject.getId(), issueUUID, attachmentName, is);
										String oneDevAttachmentUrl = oneDevProject.getAttachmentUrlPath(issueUUID, oneDevAttachmentName);
										if (markdown.contains("(" + attachmentName + ")")) {
											markdown = markdown.replace("(" + attachmentName + ")", "(" + oneDevAttachmentUrl + ")");
										} else {
											unreferencedAttachments.put(attachmentName, oneDevAttachmentUrl);
										}
									} catch (IOException e) {
										throw new RuntimeException(e);
									}
								}
							}

View on GitHub (pinned to d44925c47c)

Solutions

  1. Check the embedded error message/URL: fix the underlying HTTP cause (auth, 404, 5xx) and re-run the import.
  2. Verify the YouTrack token is valid and has permission to read attachments.
  3. Confirm the YouTrack base URL in connection settings is correct and reachable from the OneDev server.
  4. Retry the import later if it was a transient 5xx/rate limit; remove re-uploaded duplicates if resuming.
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight: verify token and attachment endpoint reachability
try (Response r = client.target(base + "/api/users/me").request().get()) {
    if (r.getStatus() != 200) throw new IllegalStateException("YouTrack token invalid or insufficient");
}

Try / catch

try { processAttachments(...); } catch (ExplicitException e) {
    // message contains endpoint and API error; retry transient failures
    logger.error("Attachment download failed: {}", e.getMessage());
}

Prevention

When it happens

Trigger: GET request to the constructed attachment endpoint returns a non-2xx status — expired/insufficient YouTrack token (401/403), attachment deleted in YouTrack (404), server error (5xx), or rate limiting while bulk-downloading attachments.

Common situations: Token permissions revoked mid-import; attachment removed from YouTrack between listing and downloading; YouTrack server overload or temporary 502 from a proxy; base URL misconfigured so the endpoint resolves wrong.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/0a982d51cdae76ae. Report an issue: GitHub.