spring-projects/spring-ai · error · RuntimeException
java.io.IOException
Error message
java.io.IOException
What it means
PromptUserSpec.text(Resource, Charset) reads the Resource content via getContentAsString, which can throw IOException; the client wraps it in a RuntimeException. It indicates the underlying resource (file, classpath entry, URL resource) could not be read.
Source
Thrown at spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/DefaultChatClient.java:204
return this;
}
@Override
public PromptUserSpec text(String text) {
Assert.hasText(text, "text cannot be null or empty");
this.text = text;
return this;
}
@Override
public PromptUserSpec text(Resource text, Charset charset) {
Assert.notNull(text, "text cannot be null");
Assert.notNull(charset, "charset cannot be null");
try {
this.text(text.getContentAsString(charset));
}
catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
@Override
public PromptUserSpec text(Resource text) {
Assert.notNull(text, "text cannot be null");
this.text(text, Charset.defaultCharset());
return this;
}
@Override
public PromptUserSpec param(String key, Object value) {
Assert.hasText(key, "key cannot be null or empty");
Assert.notNull(value, "value cannot be null");
this.params.put(key, value);
return this;
}View on GitHub (pinned to 98a7beda4f)
Solutions
- Verify the resource exists (resource.exists()) and is readable before passing it
- Check the classpath (src/main/resources) or absolute file path correctness
- Read the content yourself with proper error handling and pass the String via text(String)
- Catch the RuntimeException and inspect the IOException cause for the real path/message
Example fix
// before
.user(u -> u.text(new FileSystemResource("prompts/user.txt"), StandardCharsets.UTF_8)); // missing file
// after
Resource res = new FileSystemResource("prompts/user.txt");
if (res.exists()) {
.user(u -> u.text(res, StandardCharsets.UTF_8));
} else {
throw new IllegalStateException("Prompt resource missing: " + res.getFilename());
} Defensive patterns
Strategy: validation
Validate before calling
Resource r = new ClassPathResource("prompts/user.txt"); if (!r.exists() || !r.isReadable()) throw new IllegalStateException("User prompt resource missing/unreadable"); Type guard
static String safeText(Resource r, Charset cs) { try { return r.getContentAsString(cs); } catch (IOException e) { return null; } } Try / catch
try { builder.user(u -> u.text(res, charset)); } catch (RuntimeException e) { if (e.getCause() instanceof IOException io) { throw new IllegalStateException("Cannot read user prompt: " + io.getMessage(), io); } throw e; } Prevention
- Check resource.exists()/isReadable() before passing to the builder
- Read resources yourself and pass Strings for explicit error handling
- Ensure prompt files are packaged in the artifact and present in the working directory
When it happens
Trigger: Calling .user(u -> u.text(resource, charset)) where the Resource points to a missing file, unreadable location, or a stream that fails mid-read (e.g. FileSystemResource on a non-existent path, ClassPathResource not on the classpath).
Common situations: Typo'd classpath prompt file; file deleted or path relative to wrong working directory; permissions issue on a mounted volume; encoding mismatch causing read failure.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- Failed to read resource:
- Failed to cache the resource:
- java.io.IOException
- java.net.URISyntaxException
- IOException (wrapped RuntimeException)
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/270abab036be2e40.
Report an issue: GitHub.