{"record":{"id":"712ccf9746760eb9","repo":"windmill-labs/windmill","slug":"could-not-open-claude-desktop-deep-link-err-mes","errorCode":null,"errorMessage":"Could not open Claude Desktop deep link (${err.message}). Open it manually: ${deepLink}","messagePattern":"Could not open Claude Desktop deep link \\((.+?)\\)\\. Open it manually: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cli/src/commands/app/new.ts","lineNumber":864,"sourceCode":"              { cwd: absAppDir },\n              (error) => (error ? reject(error) : resolve())\n            );\n          });\n        } finally {\n          // On exec rejection control jumps to the outer catch — without this\n          // finally the spinner keeps writing to stdout and garbles output.\n          clearInterval(spinner);\n          process.stdout.write(\"\\r\" + \" \".repeat(40) + \"\\r\");\n        }\n\n        // Import the session into Claude Desktop Code mode. Use execFile so\n        // the deep link doesn't pass through a shell — `sessionId` is a UUID\n        // and absAppDir is URI-encoded inside the URL today, but execFile\n        // removes shell escaping concerns entirely.\n        const deepLink = `claude://resume?session=${sessionId}&cwd=${encodeURIComponent(absAppDir)}`;\n        execFile(\"open\", [deepLink], (err) => {\n          if (err) {\n            log.warn(\n              colors.yellow(\n                `Could not open Claude Desktop deep link (${err.message}). Open it manually: ${deepLink}`\n              )\n            );\n          } else {\n            log.info(colors.bold.green(\"Opened in Claude Desktop!\"));\n          }\n        });\n      } catch (error: unknown) {\n        const errorMessage =\n          error instanceof Error ? error.message : String(error);\n        log.warn(\n          colors.yellow(\n            `Could not open in Claude Desktop: ${errorMessage}`\n          )\n        );\n        log.info(\n          colors.gray(","sourceCodeStart":846,"sourceCodeEnd":882,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/cli/src/commands/app/new.ts#L846-L882","documentation":"Warning emitted by `wmill app new` after scaffolding, when it tries to resume/import the freshly created Claude session in Claude Desktop via the macOS `open` command with a claude:// deep link. The execFile('open', [deepLink]) callback received an error, meaning macOS failed to hand the URL to the Claude Desktop app (e.g. `open` binary missing/failed or no app registered for the claude:// scheme). The CLI suggests opening the printed deep link manually and continues.","triggerScenarios":"Running `wmill app new` on macOS with Claude Desktop detected, answering 'Open in Claude Desktop?', and execFile('open', [deepLink]) invokes its callback with an error: `open` not found or non-zero exit (no handler registered for claude://, Claude Desktop uninstalled/deregistered, or headless environment without a GUI session).","commonSituations":"Claude Desktop installed but its URL handler deregistered after an update; running over SSH without a GUI login session; macOS security prompts blocking `open`; user on Linux where /usr/bin/open is not macOS's open (e.g. openstructure) and cannot handle claude://.","solutions":["Open the printed deep link manually (copy `claude://resume?session=...&cwd=...` into a browser or Spotlight) to resume the session in Claude Desktop.","Verify Claude Desktop is installed and launched at least once so it registers the claude:// scheme, then retry `wmill app new`.","Test the handler manually: run `open 'claude://resume'` in a terminal; if it fails, re-install or re-launch Claude Desktop.","As a fallback, run `cd <appDir> && claude` in the terminal, which the CLI also suggests on the sibling error path."],"exampleFix":"// before (inside the CLI's callback path)\nexecFile(\"open\", [deepLink], (err) => { if (err) log.warn(...); });\n// after (caller-side manual fallback)\nexecFile(\"open\", [deepLink], (err) => {\n  if (err) {\n    console.log(`Open manually: ${deepLink}`);\n    // or: execFile(\"xdg-open\", [deepLink]) on Linux\n  }\n});","handlingStrategy":"try-catch","validationCode":"import { execFile } from \"node:child_process\";\nfunction canOpenDeepLinks(): Promise<boolean> {\n  return new Promise((res) =>\n    execFile(\"open\", [\"-Ra\", \"Claude\"], (err) => res(!err))\n  );\n}","typeGuard":"function isExecFileError(e: NodeJS.ErrnoException | null): e is NodeJS.ErrnoException & { code: string } {\n  return e !== null && typeof e.code === \"string\";\n}","tryCatchPattern":"execFile(\"open\", [deepLink], (err) => {\n  if (err) {\n    log.warn(`Deep link failed (${err.message}); open manually: ${deepLink}`);\n  } else {\n    log.info(\"Opened in Claude Desktop!\");\n  }\n});","preventionTips":["Launch Claude Desktop once after install/update so it re-registers the claude:// URL scheme.","Test `open 'claude://resume'` manually before scripting the desktop-open flow.","On Linux/Windows use the platform opener (xdg-open / `start`) instead of macOS `open`.","Avoid running the desktop-open step over SSH or headless sessions where no GUI handler exists."],"tags":["cli","macos","deep-link","claude-desktop","open-command"],"backgroundTag":"url-handler-not-registered","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}