spring-projects/spring-ai · error · RuntimeException
java.io.IOException
Error message
java.io.IOException
What it means
Builder.defaultUser(Resource, Charset) reads the resource text with getContentAsString(charset); an IOException (unreadable/missing resource) is wrapped and rethrown as a plain RuntimeException. The wrapping hides the underlying cause from the top-level message.
Source
Thrown at spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/DefaultChatClientBuilder.java:154
public Builder defaultOptions(ChatOptions.Builder customizer) {
this.defaultRequest.options(customizer);
return this;
}
public Builder defaultUser(String text) {
this.defaultRequest.user(text);
return this;
}
public Builder defaultUser(Resource text, Charset charset) {
Assert.notNull(text, "text cannot be null");
Assert.notNull(charset, "charset cannot be null");
try {
this.defaultRequest.user(text.getContentAsString(charset));
}
catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
public Builder defaultUser(Resource text) {
return this.defaultUser(text, Charset.defaultCharset());
}
public Builder defaultUser(Consumer<PromptUserSpec> userSpecConsumer) {
this.defaultRequest.user(userSpecConsumer);
return this;
}
public Builder defaultSystem(String text) {
this.defaultRequest.system(text);
return this;
}
View on GitHub (pinned to 98a7beda4f)
Solutions
- Check that the Resource exists and is readable before calling defaultUser (resource.exists()).
- Read e.getCause() to see the actual IOException and fix the path/stream.
- Fall back to defaultUser(String) with content loaded and validated by your own code.
- Ensure the resource is on the runtime classpath of the deployed artifact.
Example fix
// before
builder.defaultUser(new ClassPathResource("prompts/user.txt"));
// after
Resource r = new ClassPathResource("prompts/user.txt");
if (!r.exists()) throw new IllegalStateException("user prompt resource missing");
builder.defaultUser(r); Defensive patterns
Strategy: validation
Validate before calling
Resource r = new ClassPathResource("prompts/user.txt");
if (!r.exists()) throw new IllegalStateException("missing default user resource");
builder.defaultUser(r); Try / catch
try { builder.defaultUser(resource); } catch (RuntimeException e) { log.error("defaultUser resource read failed", e.getCause()); } Prevention
- Verify the resource exists at startup when building shared ChatClient beans.
- Keep prompt resources under src/main/resources so they're packaged.
- Use defaultUser(String) for content you've already loaded and validated.
When it happens
Trigger: ChatClient.builder().defaultUser(Resource text, Charset charset) (or defaultUser(Resource)) where the Resource cannot be read: nonexistent classpath/file resource, closed stream, or I/O failure.
Common situations: Building a shared ChatClient bean with a default user prompt loaded from a resource that wasn't packaged or whose path changed; charset issues with malformed input streams.
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
- httpClientBuilderCustomizers cannot be combined with a pre-b
- httpClientBuilderCustomizers cannot be combined with a pre-b
- Only outputType or outputJsonSchema can be set, not both.
- Either outputType or outputJsonSchema must be set.
- IOException (wrapped RuntimeException)
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/c5dc03da59e65a7f.
Report an issue: GitHub.