{"record":{"id":"849c0859d85a2322","repo":"stablyai/orca","slug":"unable-to-create-untitled-markdown-note","errorCode":null,"errorMessage":"Unable to create untitled markdown note","messagePattern":"Unable to create untitled markdown note","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mobile/app/h/[hostId]/session/[worktreeId].tsx","lineNumber":3843,"sourceCode":"          const message = (createResponse as RpcFailure).error.message\n          if (isFileExistsErrorMessage(message) && attempt < 100) {\n            continue\n          }\n          throw new Error(message || 'Failed to create markdown note')\n        }\n\n        const openResponse = await client.sendRequest(\n          'files.open',\n          { worktree, relativePath },\n          { timeoutMs: 15_000 }\n        )\n        if (!openResponse.ok) {\n          throw new Error((openResponse as RpcFailure).error.message)\n        }\n        scheduleDelayedAction(() => void fetchSessionTabs(), 300)\n        return\n      }\n      throw new Error('Unable to create untitled markdown note')\n    } catch (err) {\n      const message = err instanceof Error ? err.message : 'Failed to create markdown note'\n      setCreateError(message)\n      showToast(message, 1800)\n    } finally {\n      setCreatingMarkdown(false)\n    }\n  }\n\n  async function handleCreateBrowser(rawUrl = 'about:blank'): Promise<boolean> {\n    if (!client || creatingBrowser) {\n      return false\n    }\n    // Why: read via ref so a tap before the capability probe resolves (or a stale callback) still sees the live value.\n    if (browserScreencastSupportedRef.current !== true) {\n      showToast('Desktop update required for mobile browser streaming', 1600)\n      return false\n    }","sourceCodeStart":3825,"sourceCodeEnd":3861,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/app/h/[hostId]/session/[worktreeId].tsx#L3825-L3861","documentation":"Thrown after the mobile session screen exhausts 100 attempts to create an 'untitled.md' (or untitled-N.md) file in a worktree without finding a free slot. Each iteration calls the host's 'files.createFile' RPC and bails to this generic message only when the loop completes without a successful create-and-open pair. It is a fallback for the unlikely case that every untitled-1..100.md already exists or every create call failed with a non-'file exists' error that did not throw.","triggerScenarios":"Reached only when `files.createFile` returns ok=false with a message that isFileExistsErrorMessage() recognizes 100 times in a row, or when createFile succeeds but the immediately-following `files.open` fails 100 times. The worktree is referenced as `id:${worktreeId}` via the client RPC.","commonSituations":"A worktree directory pre-populated with untitled.md through untitled-100.md (e.g. a test fixture, a generated docs folder, or a sync tool), or a host that rejects every createFile for reasons other than 'file exists' (permissions, read-only FS, worktree locked by another mutation).","solutions":["Inspect the worktree directory and delete or rename the existing untitled-*.md files so a fresh slot is free.","Check the host-side logs for the actual files.createFile errors; the generic message hides the per-attempt failure because non-'file exists' failures throw earlier with the host message.","Verify the worktree is writable and not locked by another in-flight mutation (captureMobileFileMutationOwnership).","If 100 slots is genuinely too few, raise the attempt ceiling or switch to a timestamp/UUID-based filename scheme."],"exampleFix":"// before\nfor (let attempt = 1; attempt <= 100; attempt += 1) {\n  const relativePath = attempt === 1 ? 'untitled.md' : `untitled-${attempt}.md`\n  // ...\n}\nthrow new Error('Unable to create untitled markdown note')\n\n// after — include the last failure reason so the user can act on it\nlet lastReason = ''\nfor (let attempt = 1; attempt <= 100; attempt += 1) {\n  const relativePath = attempt === 1 ? 'untitled.md' : `untitled-${attempt}.md`\n  const createResponse = await client.sendRequest('files.createFile', { worktree, relativePath, ...mutationOwnership }, { timeoutMs: 15_000 })\n  if (!createResponse.ok) {\n    lastReason = (createResponse as RpcFailure).error.message\n    if (isFileExistsErrorMessage(lastReason) && attempt < 100) continue\n    throw new Error(lastReason || 'Failed to create markdown note')\n  }\n  // ...\n  return\n}\nthrow new Error(`Unable to create untitled markdown note: ${lastReason || 'all 100 slots taken'}`)","handlingStrategy":"try-catch","validationCode":"// Before invoking, pre-check the directory is not pre-saturated\nconst existing = await client.sendRequest('files.list', { worktree, relativePath: '.' }, { timeoutMs: 15_000 })\nif (existing.ok) {\n  const names = (existing as RpcSuccess).result as { entries?: { name: string }[] }\n  const taken = new Set((names.entries ?? []).map(e => e.name).filter(n => /^untitled(-\\d+)?\\.md$/.test(n)))\n  // if all 100 slots taken, prompt user for a custom name instead of looping\n}","typeGuard":"function isOpenResponseOk(r: RpcSuccess | RpcFailure): r is RpcSuccess {\n  return r.ok\n}","tryCatchPattern":"try {\n  await handleCreateMarkdownNote()\n} catch (err) {\n  const message = err instanceof Error ? err.message : 'Failed to create markdown note'\n  setCreateError(message)\n  showToast(message, 1800)\n} finally {\n  setCreatingMarkdown(false)\n}","preventionTips":["Avoid seeding worktrees with untitled-*.md files.","Consider a UUID or timestamp suffix instead of a 1..100 counter to eliminate slot exhaustion.","Surfaces the per-attempt host error in the final message so users can act on permissions/lock failures."],"tags":["rpc","filesystem","mobile","worktree","fallback-message"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}