{"record":{"id":"4a45484d294bcb92","repo":"can1357/oh-my-pi","slug":"host-uri-read-handlers-must-return-a-string-or-a-h","errorCode":null,"errorMessage":"Host URI read handlers must return a string or a HostUriReadResult mapping","messagePattern":"Host URI read handlers must return a string or a HostUriReadResult mapping","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"python/omp-rpc/src/omp_rpc/host_uris.py","lineNumber":104,"sourceCode":"    return HostUri(\n        scheme=cleaned,\n        read=read,\n        write=write,\n        description=description,\n        immutable=immutable,\n    )\n\n\ndef normalize_read_result(value: HostUriReadValue) -> JsonObject:\n    \"\"\"Convert a handler's `read` return into the wire-frame fields.\n\n    Returns a dict suitable for spreading into a `host_uri_result` payload.\n    \"\"\"\n\n    if isinstance(value, str):\n        return {\"content\": value}\n    if not isinstance(value, dict):\n        raise TypeError(\n            \"Host URI read handlers must return a string or a HostUriReadResult mapping\"\n        )\n\n    payload: JsonObject = {}\n    if \"content\" not in value:\n        raise ValueError(\"HostUriReadResult requires a 'content' field\")\n    payload[\"content\"] = str(value[\"content\"])\n\n    content_type = value.get(\"content_type\")\n    if content_type is not None:\n        if content_type not in (\"text/markdown\", \"application/json\", \"text/plain\"):\n            raise ValueError(f\"Unsupported content_type: {content_type!r}\")\n        payload[\"contentType\"] = content_type\n\n    notes = value.get(\"notes\")\n    if notes is not None:\n        payload[\"notes\"] = [str(item) for item in notes]\n","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/omp-rpc/src/omp_rpc/host_uris.py#L86-L122","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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')`"],"exampleFix":"// before\ndef read(path):\n    with open(path, 'rb') as f:\n        return f.read()  # bytes -> TypeError\n// after\ndef read(path):\n    with open(path, 'r', encoding='utf-8') as f:\n        return f.read()  # str is accepted","handlingStrategy":"type-guard","validationCode":"def check_read_result(value):\n    if isinstance(value, str) or isinstance(value, dict):\n        return value\n    raise TypeError(f'read handler must return str or dict, got {type(value).__name__}')","typeGuard":"def is_valid_read_result(value) -> bool:\n    return isinstance(value, (str, dict))","tryCatchPattern":"try:\n    result = read_handler(uri)\nexcept TypeError as e:\n    logger.error('read handler returned unsupported type: %s', e)\n    result = {'content': 'handler error'}","preventionTips":["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"],"tags":["typeerror","host-uri","read-handler","api-contract"],"backgroundTag":"invalid-handler-return-type","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}