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

  1. Unwrap ex.getCause() (IOException) to identify FileNotFoundException vs other IO problems
  2. Call resource.exists() before building the message and fail fast with a clear message
  3. Ensure the file is in src/main/resources so it lands in the classpath jar
  4. 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

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


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/e52351ed5d6d089d. Report an issue: GitHub.