OpenAPITools/openapi-generator · error · ResponseStatusException
File not found
Error message
File not found
What it means
Thrown by GET /api/gen/download/{fileId} when the fileMap entry exists and is fresh, but Files.readAllBytes (GenApiService.java:103) throws FileNotFoundException because the zip is no longer on disk. The key detail is that downloadFile deletes the file's parent directory right after a successful read (GenApiService.java:110), which makes every fileId strictly one-shot: the first GET succeeds and destroys the artifact, so any second GET for the same id fails here even within the 24h TTL.
Source
Thrown at modules/openapi-generator-online/src/main/java/org/openapitools/codegen/online/service/GenApiService.java:105
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());
}
return ResponseEntity
.ok()
.contentType(MediaType.valueOf("application/zip"))
.contentLength(resource.contentLength())
.header("Content-Disposition",
"attachment; filename=\"" + g.getFriendlyName() + "-generated.zip\"")
.header("Accept-Range", "bytes")
.body(resource);
}
View on GitHub (pinned to fcec517be3)
Solutions
- Treat every download link as single-use: never re-GET a fileId that already returned 200
- If you need the artifact again, call the generate endpoint to obtain a fresh fileId
- On 404 from download, fall back to regenerate-then-download rather than re-fetching the same URL
- If self-hosting, keep tmp cleanup jobs away from codegen-tmp-* directories
Example fix
# before
curl -sOJ "$host/api/gen/download/$code" # 200, and the server deletes the file
curl -sOJ "$host/api/gen/download/$code" # 404 File not found (one-shot link)
# after
if [ ! -f "$code.zip" ]; then
curl -sOJ "$host/api/gen/download/$code" # fetch once, keep the local copy
fi
# need it again? generate a new one
code=$(curl -s -X POST "$host/api/gen/clients/java" -H 'Content-Type: application/json' \
-d "{\"spec\":$SPEC}" | sed -E 's/.*"code":"([^"]+)".*/\1/') Defensive patterns
Strategy: validation
Validate before calling
// links are one-shot: the server deletes the artifact after the first 200
Set<String> consumed = ConcurrentHashMap.newKeySet();
if (!consumued.add(fileId)) {
throw new IllegalStateException("fileId already downloaded once; request a new generation");
} Try / catch
catch (HttpClientErrorException.NotFound e) {
if (e.getResponseBodyAsString().contains("File not found")) {
// map entry fresh but zip gone -> it was already downloaded; regenerate
fileId = generate(host, language, spec);
}
} Prevention
- Save the downloaded zip locally instead of re-fetching the link
- Disable automatic retry/refresh on the download URL in browsers and HTTP clients
- Treat the download response as the artifact of record, not the URL
When it happens
Trigger: Calling GET /api/gen/download/{code} a second time for the same fileId (the first call already deleted the directory); an external process/cron wiping java.io.tmpdir where codegen-tmp-* directories live; the hourly cleaner racing between the map lookup and the read.
Common situations: Retry logic in scripts that re-fetch the same link after a flaky network response; two team members (or two jobs) sharing one generated link; browsers or proxies that re-issue the GET; curl without -o writing the first attempt somewhere unexpected and the user re-running it.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- File not found or has expired
- Unsupported target %s supplied. %s
- {name}
- Spec directory doesn't contain any specification
- I/O error while reading file
AI-assisted analysis of OpenAPITools/openapi-generator@fcec517be3 (2026-08-22).
Data as JSON: /api/errors/525f6a9a9158c0ce.
Report an issue: GitHub.