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.

Solutions

  1. Treat every download link as single-use: never re-GET a fileId that already returned 200
  2. If you need the artifact again, call the generate endpoint to obtain a fresh fileId
  3. On 404 from download, fall back to regenerate-then-download rather than re-fetching the same URL
  4. 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

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


AI-assisted analysis of OpenAPITools/openapi-generator@fcec517be3 (2026-08-22). Data as JSON: /api/errors/525f6a9a9158c0ce. Report an issue: GitHub.

Appendix: 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)