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

  1. Regenerate (POST /api/gen/clients/{language} or /api/gen/servers/{framework}) and immediately download the new fileId
  2. If you self-host behind a load balancer, enable sticky sessions or pin generate+download to the same instance
  3. Keep generate-then-download inside the same script/job run so the elapsed time stays far below 24h
  4. 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

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


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