{"record":{"id":"525f6a9a9158c0ce","repo":"OpenAPITools/openapi-generator","slug":"file-not-found","errorCode":null,"errorMessage":"File not found","messagePattern":"File not found","errorType":"http","errorClass":"ResponseStatusException","httpStatus":404,"severity":"error","filePath":"modules/openapi-generator-online/src/main/java/org/openapitools/codegen/online/service/GenApiService.java","lineNumber":105,"sourceCode":"        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        }\n        return ResponseEntity\n                .ok()\n                .contentType(MediaType.valueOf(\"application/zip\"))\n                .contentLength(resource.contentLength())\n                .header(\"Content-Disposition\",\n                        \"attachment; filename=\\\"\" + g.getFriendlyName() + \"-generated.zip\\\"\")\n                .header(\"Accept-Range\", \"bytes\")\n                .body(resource);\n    }\n","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/OpenAPITools/openapi-generator/blob/fcec517be3cf5b7964296bcba25fbc97541484e7/modules/openapi-generator-online/src/main/java/org/openapitools/codegen/online/service/GenApiService.java#L87-L123","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\ncurl -sOJ \"$host/api/gen/download/$code\"   # 200, and the server deletes the file\ncurl -sOJ \"$host/api/gen/download/$code\"   # 404 File not found (one-shot link)\n\n# after\nif [ ! -f \"$code.zip\" ]; then\n  curl -sOJ \"$host/api/gen/download/$code\"   # fetch once, keep the local copy\nfi\n# need it again? generate a new one\ncode=$(curl -s -X POST \"$host/api/gen/clients/java\" -H 'Content-Type: application/json' \\\n  -d \"{\\\"spec\\\":$SPEC}\" | sed -E 's/.*\"code\":\"([^\"]+)\".*/\\1/')","handlingStrategy":"validation","validationCode":"// links are one-shot: the server deletes the artifact after the first 200\nSet<String> consumed = ConcurrentHashMap.newKeySet();\nif (!consumued.add(fileId)) {\n    throw new IllegalStateException(\"fileId already downloaded once; request a new generation\");\n}","typeGuard":null,"tryCatchPattern":"catch (HttpClientErrorException.NotFound e) {\n    if (e.getResponseBodyAsString().contains(\"File not found\")) {\n        // map entry fresh but zip gone -> it was already downloaded; regenerate\n        fileId = generate(host, language, spec);\n    }\n}","preventionTips":["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"],"tags":["http-404","file-not-found","one-shot-download","tmp-cleanup","openapi-generator-online"],"backgroundTag":"file-not-found","analyzedSha":"fcec517be3cf5b7964296bcba25fbc97541484e7","analyzedAt":"2026-08-22T11:13:11.613Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}