spring-projects/spring-ai · error · RuntimeException
Failed to read resource
Error message
Failed to read resource
What it means
MessageUtils.readResource reads a Spring Resource fully into a String using the given charset; any IOException is wrapped in a RuntimeException with message 'Failed to read resource' and the original exception as cause. It is the low-level loader used by SystemMessage/UserMessage builders to read media/text resources.
Source
Thrown at spring-ai-model/src/main/java/org/springframework/ai/chat/messages/MessageUtils.java:48
* @author Thomas Vitale
*/
final class MessageUtils {
private MessageUtils() {
}
static String readResource(Resource resource) {
return readResource(resource, Charset.defaultCharset());
}
static String readResource(Resource resource, Charset charset) {
Assert.notNull(resource, "resource cannot be null");
Assert.notNull(charset, "charset cannot be null");
try (InputStream inputStream = resource.getInputStream()) {
return StreamUtils.copyToString(inputStream, charset);
}
catch (IOException ex) {
throw new RuntimeException("Failed to read resource", ex);
}
}
}
View on GitHub (pinned to 98a7beda4f)
Solutions
- Unwrap ex.getCause() (IOException) to identify FileNotFoundException vs other IO problems
- Call resource.exists() before building the message and fail fast with a clear message
- Ensure the file is in src/main/resources so it lands in the classpath jar
- For external files, verify the absolute path and permissions at startup
Example fix
// before
SystemMessage msg = SystemMessage.builder().resource(new ClassPathResource("prompts/sys.txt")).build();
// after
Resource r = new ClassPathResource("prompts/sys.txt");
Assert.isTrue(r.exists(), "prompts/sys.txt not found on classpath");
SystemMessage msg = SystemMessage.builder().resource(r).build(); Defensive patterns
Strategy: validation
Validate before calling
Resource r = new ClassPathResource("prompts/sys.txt");
if (!r.exists()) throw new IllegalStateException("Missing resource: prompts/sys.txt"); Try / catch
try {
String text = MessageUtils.readResource(resource, StandardCharsets.UTF_8);
} catch (RuntimeException e) {
throw new IllegalStateException("Cannot read " + resource + ": " + e.getCause().getMessage(), e.getCause());
} Prevention
- Check resource.exists() before building messages
- Keep prompt files in src/main/resources
- Fail fast at application startup by loading required prompts once
When it happens
Trigger: Building a SystemMessage/UserMessage from a Resource whose stream cannot be opened: missing classpath entry, wrong URL, closed filesystem, or inaccessible file.
Common situations: Passing new ClassPathResource("prompts/system.txt") where the file is missing from the jar; loading from an external file:// path that doesn't exist at runtime; container images without the bundled resource.
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
- IOException (wrapped RuntimeException)
- Failed to read resource:
- Failed to cache the resource:
- java.io.IOException
- java.io.IOException
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/e52351ed5d6d089d.
Report an issue: GitHub.