OpenAPITools/openapi-generator · error · ResponseStatusException
I/O error while reading file
Error message
I/O error while reading file
What it means
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.
Source
Thrown at modules/openapi-generator-online/src/main/java/org/openapitools/codegen/online/service/GenApiService.java:107
@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);
}
@Override
public ResponseEntity<ResponseCode> generateClient(String language, GeneratorInput generatorInput) {View on GitHub (pinned to fcec517be3)
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
Example fix
# before
curl -fOJ "$host/api/gen/download/$code" || exit 1 # 500 I/O error aborts the pipeline
# after: one regenerate+download attempt, then give up with a clear message
zip=$(curl -sfOJ "$host/api/gen/download/$code" && echo ok)
if [ -z "$zip" ]; then
code=$(curl -s -X POST "$host/api/gen/clients/java" -H 'Content-Type: application/json' \
-d "{\"spec\":$SPEC}" | sed -E 's/.*"code":"([^"]+)".*/\1/')
curl -fOJ "$host/api/gen/download/$code" || { echo "generator download failed twice" >&2; exit 1; }
fi Defensive patterns
Strategy: retry
Try / catch
catch (HttpServerErrorException e) {
// transient server-side fs race: one fresh generate+download usually clears it
if (e.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR
&& e.getResponseBodyAsString().contains("I/O error while reading file")) {
fileId = generate(host, language, spec);
retryDownload(fileId);
} else { throw e; }
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cannot access tmp folder
- Failed to generate .openapi-generator-ignore when the option
- Could not generate supporting file '{ignoreFileNameTarget}'
- Target files must be generated within the output directory;
- Could not generate supporting file '{versionMetadata}'
AI-assisted analysis of OpenAPITools/openapi-generator@fcec517be3 (2026-08-22).
Data as JSON: /api/errors/4249459315ad9196.
Report an issue: GitHub.