{"record":{"id":"7176d2aa7c9b0d4a","repo":"OpenAPITools/openapi-generator","slug":"file-not-found-or-has-expired","errorCode":null,"errorMessage":"File not found or has expired","messagePattern":"File not found or has expired","errorType":"http","errorClass":"ResponseStatusException","httpStatus":404,"severity":"warning","filePath":"modules/openapi-generator-online/src/main/java/org/openapitools/codegen/online/service/GenApiService.java","lineNumber":95,"sourceCode":"\n        clients.sort(String.CASE_INSENSITIVE_ORDER);\n        servers.sort(String.CASE_INSENSITIVE_ORDER);\n    }\n\n    @Autowired\n    private NativeWebRequest request;\n\n    @Override\n    public Optional<NativeWebRequest> getRequest() {\n        return Optional.ofNullable(request);\n    }\n\n    @Override\n    public ResponseEntity<Resource> downloadFile(String fileId) {\n        Generated g = fileMap.get(fileId);\n        LOGGER.debug(\"looking for fileId {}\", fileId);\n        if (g == null || g.getCreatedAt().plusMillis(FILE_TTL_MS).isBefore(Instant.now())) {\n            throw new ResponseStatusException(HttpStatus.NOT_FOUND, \"File not found or has expired\");\n        }\n        LOGGER.debug(\"got filename {}\", g.getFilename());\n\n        File file = new File(g.getFilename());\n        Path path = Paths.get(file.getAbsolutePath());\n        ByteArrayResource resource;\n        try {\n            resource = new ByteArrayResource(Files.readAllBytes(path));\n        } catch (FileNotFoundException e) {\n            throw new ResponseStatusException(HttpStatus.NOT_FOUND, \"File not found\", e);\n        } catch (IOException e) {\n            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, \"I/O error while reading file\", e);\n        }\n        try {\n            FileUtils.deleteDirectory(file.getParentFile());\n        } catch (IOException e) {\n            LOGGER.error(\"failed to delete file {}\", file.getAbsolutePath());\n        }","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/OpenAPITools/openapi-generator/blob/fcec517be3cf5b7964296bcba25fbc97541484e7/modules/openapi-generator-online/src/main/java/org/openapitools/codegen/online/service/GenApiService.java#L77-L113","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\n# link saved yesterday (or after a server restart)\ncurl -sOJ \"$GENERATOR_HOST/api/gen/download/$OLD_CODE\"   # 404 File not found or has expired\n\n# after\n# generate and download in the same run\ncode=$(curl -s -X POST \"$GENERATOR_HOST/api/gen/clients/java\" \\\n  -H 'Content-Type: application/json' -d '{\"spec\":{}}' | sed -E 's/.*\"code\":\"([^\"]+)\".*/\\1/')\ncurl -sOJ \"$GENERATOR_HOST/api/gen/download/$code\"","handlingStrategy":"try-catch","validationCode":"// track when you generated, before trusting the link (TTL is 24h server-side)\nInstant generatedAt = Instant.now();\nif (generatedAt.plus(Duration.ofHours(23)).isBefore(Instant.now())) {\n    throw new IllegalStateException(\"download link is stale (>24h), regenerate first\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    restTemplate.getForObject(host + \"/api/gen/download/{code}\", Resource.class, code);\n} catch (HttpClientErrorException.NotFound e) {\n    // one-shot / TTL semantics: the only sane recovery is regenerate + re-download\n    code = generate(host, language, spec);\n    restTemplate.getForObject(host + \"/api/gen/download/{code}\", Resource.class, code);\n}","preventionTips":["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"],"tags":["http-404","download-link","ttl-expiry","in-memory-state","openapi-generator-online"],"backgroundTag":"expired-download-link","analyzedSha":"fcec517be3cf5b7964296bcba25fbc97541484e7","analyzedAt":"2026-08-22T11:13:11.613Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}