{"record":{"id":"4249459315ad9196","repo":"OpenAPITools/openapi-generator","slug":"i-o-error-while-reading-file","errorCode":null,"errorMessage":"I/O error while reading file","messagePattern":"I/O error while reading file","errorType":"http","errorClass":"ResponseStatusException","httpStatus":500,"severity":"error","filePath":"modules/openapi-generator-online/src/main/java/org/openapitools/codegen/online/service/GenApiService.java","lineNumber":107,"sourceCode":"\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\n    @Override\n    public ResponseEntity<ResponseCode> generateClient(String language, GeneratorInput generatorInput) {","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/OpenAPITools/openapi-generator/blob/fcec517be3cf5b7964296bcba25fbc97541484e7/modules/openapi-generator-online/src/main/java/org/openapitools/codegen/online/service/GenApiService.java#L89-L125","documentation":"Thrown by GET /api/gen/download/{fileId} when Files.readAllBytes (GenApiService.java:103) fails with an IOException other than FileNotFoundException, e.g. EACCES/permission denial, the path being unreadable, or an OS-level I/O error. The fileMap entry passed both the existence and TTL checks, so this points at server-side filesystem state rather than client input. It maps to HTTP 500 because the caller did nothing wrong.","triggerScenarios":"The tmp directory or zip file permissions changed between generation and download; the directory was deleted by the hourly cleaner or another process in the race window after the map lookup but before/during the read; tmpfs exhaustion or disk fault while reading; security policy (SELinux/AppArmor) blocking reads from java.io.tmpdir.","commonSituations":"Containers where /tmp is a size-limited tmpfs that evicts files under pressure; hardened hosts restricting the service user's tmp access; multiple instances sharing a volume while fileMap is per-JVM; disk-full conditions on the server.","solutions":["Retry once by regenerating and downloading a fresh fileId (transient races with the cleaner resolve themselves)","If self-hosting, verify the service user can read java.io.tmpdir/codegen-tmp-* (ls -ld, run as that user)","Mount a dedicated writable volume or tmpfs at the tmp location with enough size for generated zips","Check server logs for the stack trace carried in the ResponseStatusException cause to confirm the exact errno"],"exampleFix":"# before\ncurl -fOJ \"$host/api/gen/download/$code\" || exit 1   # 500 I/O error aborts the pipeline\n\n# after: one regenerate+download attempt, then give up with a clear message\nzip=$(curl -sfOJ \"$host/api/gen/download/$code\" && echo ok)\nif [ -z \"$zip\" ]; then\n  code=$(curl -s -X POST \"$host/api/gen/clients/java\" -H 'Content-Type: application/json' \\\n    -d \"{\\\"spec\\\":$SPEC}\" | sed -E 's/.*\"code\":\"([^\"]+)\".*/\\1/')\n  curl -fOJ \"$host/api/gen/download/$code\" || { echo \"generator download failed twice\" >&2; exit 1; }\nfi","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (HttpServerErrorException e) {\n    // transient server-side fs race: one fresh generate+download usually clears it\n    if (e.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR\n            && e.getResponseBodyAsString().contains(\"I/O error while reading file\")) {\n        fileId = generate(host, language, spec);\n        retryDownload(fileId);\n    } else { throw e; }\n}","preventionTips":["Alert on repeated 500s from /api/gen/download — that is a server tmp/filesystem problem, not client error","Self-hosting: keep tmp cleaners away from codegen-tmp-* and monitor tmp space/permissions","Mount a dedicated writable volume for tmp in containerized deployments"],"tags":["http-500","io-error","filesystem","tmp-permissions","openapi-generator-online"],"backgroundTag":"file-read-io-error","analyzedSha":"fcec517be3cf5b7964296bcba25fbc97541484e7","analyzedAt":"2026-08-22T11:13:11.613Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}