OpenAPITools/openapi-generator · warning · ResponseStatusException
File not found or has expired
Error message
File not found or has expired
What it means
Thrown by GET /api/gen/download/{fileId} in the online generator when the fileId is absent from the in-memory fileMap or its entry is older than FILE_TTL_MS (24 hours, GenApiService.java:61). fileMap is a static ConcurrentHashMap that only lives as long as the JVM, and a @Scheduled job evicts entries older than the TTL every hour. So the download link returned by POST /api/gen/clients|servers is only valid for 24 hours and only on the exact server instance that created it.
Source
Thrown at modules/openapi-generator-online/src/main/java/org/openapitools/codegen/online/service/GenApiService.java:95
clients.sort(String.CASE_INSENSITIVE_ORDER);
servers.sort(String.CASE_INSENSITIVE_ORDER);
}
@Autowired
private NativeWebRequest request;
@Override
public Optional<NativeWebRequest> getRequest() {
return Optional.ofNullable(request);
}
@Override
public ResponseEntity<Resource> downloadFile(String fileId) {
Generated g = fileMap.get(fileId);
LOGGER.debug("looking for fileId {}", fileId);
if (g == null || g.getCreatedAt().plusMillis(FILE_TTL_MS).isBefore(Instant.now())) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "File not found or has expired");
}
LOGGER.debug("got filename {}", g.getFilename());
File file = new File(g.getFilename());
Path path = Paths.get(file.getAbsolutePath());
ByteArrayResource resource;
try {
resource = new ByteArrayResource(Files.readAllBytes(path));
} catch (FileNotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "File not found", e);
} catch (IOException e) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "I/O error while reading file", e);
}
try {
FileUtils.deleteDirectory(file.getParentFile());
} catch (IOException e) {
LOGGER.error("failed to delete file {}", file.getAbsolutePath());
}View on GitHub (pinned to fcec517be3)
Solutions
- Regenerate (POST /api/gen/clients/{language} or /api/gen/servers/{framework}) and immediately download the new fileId
- If you self-host behind a load balancer, enable sticky sessions or pin generate+download to the same instance
- Keep generate-then-download inside the same script/job run so the elapsed time stays far below 24h
- Treat HTTP 404 from /api/gen/download as a 'regenerate' signal, never as a retry-forever condition
Example fix
# before
# link saved yesterday (or after a server restart)
curl -sOJ "$GENERATOR_HOST/api/gen/download/$OLD_CODE" # 404 File not found or has expired
# after
# generate and download in the same run
code=$(curl -s -X POST "$GENERATOR_HOST/api/gen/clients/java" \
-H 'Content-Type: application/json' -d '{"spec":{}}' | sed -E 's/.*"code":"([^"]+)".*/\1/')
curl -sOJ "$GENERATOR_HOST/api/gen/download/$code" Defensive patterns
Strategy: try-catch
Validate before calling
// track when you generated, before trusting the link (TTL is 24h server-side)
Instant generatedAt = Instant.now();
if (generatedAt.plus(Duration.ofHours(23)).isBefore(Instant.now())) {
throw new IllegalStateException("download link is stale (>24h), regenerate first");
} Try / catch
try {
restTemplate.getForObject(host + "/api/gen/download/{code}", Resource.class, code);
} catch (HttpClientErrorException.NotFound e) {
// one-shot / TTL semantics: the only sane recovery is regenerate + re-download
code = generate(host, language, spec);
restTemplate.getForObject(host + "/api/gen/download/{code}", Resource.class, code);
} Prevention
- Keep generate and download in the same script/CI step
- Never persist download links across deploys — fileMap dies with the JVM
- With multiple replicas, use sticky sessions so download reaches the node that generated
When it happens
Trigger: GET /api/gen/download/{code} with a mistyped or truncated UUID; downloading more than 24h after the generate call; any service restart/redeploy between generate and download (map is lost); multiple replicas behind a load balancer where the download lands on a node that never ran the generate; the hourly cleanExpiredFiles sweep removed the entry first.
Common situations: CI pipelines that persist the download link and fetch it in a later stage; scripts that cache links across runs; self-hosted setups scaled horizontally without sticky sessions; long-lived browser tabs where the user clicks an old download link.
Related errors
- File not found
- Unsupported target %s supplied. %s
- I/O error while reading file
- Framework is required
- No options were supplied
AI-assisted analysis of OpenAPITools/openapi-generator@fcec517be3 (2026-08-22).
Data as JSON: /api/errors/7176d2aa7c9b0d4a.
Report an issue: GitHub.