Kuberwastaken/claurst · warning
rmcp read_resource ' ' returned no contents
Error message
rmcp read_resource '{}' returned no contents What it means
Raised in `RmcpClientBackend::read_resource` after a SUCCESSFUL `resources/read` round-trip: the server returned a result whose `contents` array is empty, and since this backend returns a single `ResourceContents`, `.into_iter().next()` finds nothing and this explicit error is thrown. Unlike error 196 this is not a transport/protocol failure — the server answered, but had no content for the URI. The URI is embedded in the message.
Solutions
- Treat empty-contents as a legitimate outcome: match on this error and return an empty/default value instead of failing.
- Verify on the server side whether the resource backing data is empty or generation failed.
- If the resource should have content, check the server logs for errors in its resource provider.
- Prefer checking the resource's metadata (size/mime) from list_resources before reading.
Example fix
// before: any read failure aborts the pipeline
let contents = backend.read_resource(uri).await?;
// after: accept empty resources as empty content
let contents = match backend.read_resource(uri).await {
Ok(c) => c,
Err(e) if e.to_string().ends_with("returned no contents") => ResourceContents::empty(),
Err(e) => return Err(e),
}; Defensive patterns
Strategy: try-catch
Try / catch
match backend.read_resource(uri).await {
Ok(contents) => contents,
Err(e) if e.to_string().ends_with("returned no contents") => {
tracing::info!(uri, "resource has no content; using empty default");
ResourceContents::default()
}
Err(e) => return Err(e),
} Prevention
- Treat empty contents as a valid outcome for resources backed by empty or on-demand data.
- Check resource metadata (size, mime) from list_resources before reading where available.
- Never assume a listed resource always yields at least one content part.
- If a resource should have content, investigate the server-side provider rather than retrying the read.
When it happens
Trigger: Calling `read_resource(uri)` for a resource the server knows about but that currently has no content: an empty file backing the resource, a resource whose generation failed server-side, a legitimately empty resource (empty log, empty query result), or a server bug returning `contents: []` instead of an error.
Common situations: Reading an empty text file exposed as an MCP resource; reading a resource computed on demand where the underlying source returned nothing; MCP servers that model 'not found yet' as an empty contents list rather than a JSON-RPC error; pipelines that assume every listed resource has at least one content part.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- rmcp list_resources failed
- rmcp read_resource ' ' failed
- MCP server ' ': tool ' ' call failed
- rmcp list_tools failed
- rmcp call_tool ' ' failed
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/dbc2e26bd73a1ae2.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/mcp/src/rmcp_backend.rs:548
let resources = self
.peer
.list_all_resources()
.await
.map_err(|e| anyhow::anyhow!("rmcp list_resources failed: {}", e))?;
Ok(resources.into_iter().map(convert_resource).collect())
}
async fn read_resource(&self, uri: &str) -> anyhow::Result<ResourceContents> {
let result = self
.peer
.read_resource(rmcp_model::ReadResourceRequestParams::new(uri.to_string()))
.await
.map_err(|e| anyhow::anyhow!("rmcp read_resource '{}' failed: {}", uri, e))?;
let first = result
.contents
.into_iter()
.next()
.ok_or_else(|| anyhow::anyhow!("rmcp read_resource '{}' returned no contents", uri))?;
Ok(convert_resource_contents(first))
}
async fn list_prompts(&self) -> anyhow::Result<Vec<McpPrompt>> {
let prompts = self
.peer
.list_all_prompts()
.await
.map_err(|e| anyhow::anyhow!("rmcp list_prompts failed: {}", e))?;
Ok(prompts.into_iter().map(convert_prompt).collect())
}
async fn get_prompt(
&self,
name: &str,
arguments: Option<HashMap<String, String>>,
) -> anyhow::Result<GetPromptResult> {
let mut params = rmcp_model::GetPromptRequestParams::new(name.to_string());View on GitHub (pinned to b0637c97ec)