can1357/oh-my-pi · error · TypeError
Host URI read handlers must return a string or a HostUriRead
Error message
Host URI read handlers must return a string or a HostUriReadResult mapping
What it means
normalize_read_result() converts a read handler's return value into the payload dict spread into a host_uri_result message. It accepts a plain string (wrapped as {'content': value}) or a dict; anything else (None, bytes, dataclass, list) raises TypeError. The library enforces this contract because the RPC wire format can only carry a known JSON shape for read results.
Source
Thrown at python/omp-rpc/src/omp_rpc/host_uris.py:104
return HostUri(
scheme=cleaned,
read=read,
write=write,
description=description,
immutable=immutable,
)
def normalize_read_result(value: HostUriReadValue) -> JsonObject:
"""Convert a handler's `read` return into the wire-frame fields.
Returns a dict suitable for spreading into a `host_uri_result` payload.
"""
if isinstance(value, str):
return {"content": value}
if not isinstance(value, dict):
raise TypeError(
"Host URI read handlers must return a string or a HostUriReadResult mapping"
)
payload: JsonObject = {}
if "content" not in value:
raise ValueError("HostUriReadResult requires a 'content' field")
payload["content"] = str(value["content"])
content_type = value.get("content_type")
if content_type is not None:
if content_type not in ("text/markdown", "application/json", "text/plain"):
raise ValueError(f"Unsupported content_type: {content_type!r}")
payload["contentType"] = content_type
notes = value.get("notes")
if notes is not None:
payload["notes"] = [str(item) for item in notes]
View on GitHub (pinned to 9690622007)
Solutions
- Return a plain string from the read handler, e.g. `return text` — it is wrapped as {'content': text} automatically
- If you need metadata, return a dict with at least a 'content' key: {'content': text, 'content_type': 'text/plain'}
- Decode bytes before returning: `return data.decode('utf-8')`
Example fix
// before
def read(path):
with open(path, 'rb') as f:
return f.read() # bytes -> TypeError
// after
def read(path):
with open(path, 'r', encoding='utf-8') as f:
return f.read() # str is accepted Defensive patterns
Strategy: type-guard
Validate before calling
def check_read_result(value):
if isinstance(value, str) or isinstance(value, dict):
return value
raise TypeError(f'read handler must return str or dict, got {type(value).__name__}') Type guard
def is_valid_read_result(value) -> bool:
return isinstance(value, (str, dict)) Try / catch
try:
result = read_handler(uri)
except TypeError as e:
logger.error('read handler returned unsupported type: %s', e)
result = {'content': 'handler error'} Prevention
- Ensure read handlers always return (never fall off the end returning None)
- Decode bytes to str before returning from handlers
- Keep handlers async-consistent: if host_uri expects a sync result, don't return a coroutine
- Add a unit test asserting each handler's return type is str or dict
When it happens
Trigger: Registering a read handler with host_uri(read=...) whose function returns None implicitly, returns bytes from a file read, returns a custom result object/dataclass, or a handler written with an async return (returning a coroutine) that was awaited incorrectly.
Common situations: Handler does `return` with no value on an empty file path; handler returns bytes from `f.read()` on binary data; handler wraps the result in a custom class; upgrading code that previously returned None and printed instead.
Related errors
- HostUriReadResult requires a 'content' field
- Cannot SigV4-sign ${init.body.constructor?.name ?? typeof in
- Kilo device authorization response missing required fields
- Z.ai business login returned no access token
- Z.ai key provisioning returned no apiKey
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/4a45484d294bcb92.
Report an issue: GitHub.