{"record":{"id":"5270ad3e097a538e","repo":"stablyai/orca","slug":"unable-to-read-markdown","errorCode":null,"errorMessage":"Unable to read markdown","messagePattern":"Unable to read markdown","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mobile/app/h/[hostId]/session/[worktreeId].tsx","lineNumber":1863,"sourceCode":"              baseVersion: result.version,\n              isDirty: false,\n              editable: result.editable === true,\n              stale: result.isDirty,\n              readOnlyReason: result.readOnlyReason\n            })\n          )\n          return\n        }\n        if (!shouldReadMarkdownFromDiskAfterReadTabFailure(response as RpcFailure)) {\n          throw new Error((response as RpcFailure).error.message)\n        }\n        // Why: a headless host fails markdown.readTab (renderer_unavailable); fall back to the on-disk file for read-only render.\n        const fallback = await client.sendRequest('files.read', {\n          worktree: `id:${worktreeId}`,\n          relativePath: tab.relativePath\n        })\n        if (!fallback.ok) {\n          throw new Error('Unable to read markdown')\n        }\n        const fileResult = (fallback as RpcSuccess).result as {\n          content: string\n          truncated: boolean\n          byteLength: number\n        }\n        setMarkdownDocs((prev) =>\n          new Map(prev).set(\n            tab.id,\n            buildMarkdownDiskFallbackDoc({\n              content: fileResult.content,\n              truncated: fileResult.truncated,\n              tabIsDirty: tab.isDirty\n            })\n          )\n        )\n      } catch {\n        setMarkdownDocs((prev) =>","sourceCodeStart":1845,"sourceCodeEnd":1881,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/app/h/[hostId]/session/[worktreeId].tsx#L1845-L1881","documentation":"Thrown in readMarkdownTab() when the on-disk fallback (files.read) fails after markdown.readTab returned a recoverable failure (renderer_unavailable). The mobile client first tries markdown.readTab (rich rendering via desktop renderer); if that fails with renderer_unavailable on a headless host, it falls back to files.read for raw content. This error means both paths failed — the file itself could not be read.","triggerScenarios":"markdown.readTab fails with renderer_unavailable (shouldReadMarkdownFromDiskAfterReadTabFailure returns true), then client.sendRequest('files.read', {...}) returns with ok: false. The file is unreadable at the RPC level — file deleted between tab open and read, permission denied, or the worktree path is invalid.","commonSituations":"A headless/SSH desktop host where the renderer is unavailable AND the file was deleted or moved; a race condition where the tab's relativePath no longer exists; the worktree ID being stale after a workspace change; a remote host with filesystem access issues.","solutions":["Verify the file still exists at tab.relativePath in the worktree — it may have been deleted or moved.","Check the underlying files.read RPC error (currently discarded — the error is a generic string) by inspecting the fallback response.","If on a remote/headless host, confirm the worktree is still mounted and accessible.","Refresh the session tabs to get current file paths, then retry."],"exampleFix":"// before: generic error hides the RPC failure detail\nif (!fallback.ok) {\n  throw new Error('Unable to read markdown')\n}\n\n// after: surface the actual failure reason for debugging\nif (!fallback.ok) {\n  throw new Error(\n    `Unable to read markdown: ${(fallback as RpcFailure).error.message}`\n  )\n}","handlingStrategy":"fallback","validationCode":"async function canReadFile(client, worktreeId, relativePath) {\n  const probe = await client.sendRequest('files.read', {\n    worktree: `id:${worktreeId}`, relativePath\n  })\n  return probe.ok\n}","typeGuard":"function isFilesReadResult(value) {\n  return value !== null && typeof value === 'object' &&\n    typeof value.content === 'string' &&\n    typeof value.truncated === 'boolean'\n}","tryCatchPattern":"try {\n  const fallback = await client.sendRequest('files.read', { worktree, relativePath })\n  if (!fallback.ok) {\n    throw new Error(\n      `Unable to read markdown: ${(fallback as RpcFailure).error.message}`\n    )\n  }\n} catch {\n  setMarkdownDocs((prev) => new Map(prev).set(tab.id, { status: 'error', message: \"Couldn't load markdown\" }))\n}","preventionTips":["Surface the RPC error message instead of a generic string to speed up diagnosis.","Refresh session tabs before reading to ensure relativePath is current.","On headless hosts, prefer files.read as the primary path if the renderer is known to be unavailable."],"tags":["mobile","rpc","markdown","fallback","filesystem","headless"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}