Title: Extensions fail to load — SEA cache directory path mismatch (universal/ vs darwin-arm64/) · Issue #2890 · github/copilot-cli · GitHub
Open Graph Title: Extensions fail to load — SEA cache directory path mismatch (universal/ vs darwin-arm64/) · Issue #2890 · github/copilot-cli
X Title: Extensions fail to load — SEA cache directory path mismatch (universal/ vs darwin-arm64/) · Issue #2890 · github/copilot-cli
Description: Describe the bug Project extensions (.github/extensions/) fail to load when both universal/ and platform-specific (e.g. darwin-arm64/) cache directories exist under ~/Library/Caches/copilot/pkg/. The extension subprocess immediately exit...
Open Graph Description: Describe the bug Project extensions (.github/extensions/) fail to load when both universal/ and platform-specific (e.g. darwin-arm64/) cache directories exist under ~/Library/Caches/copilot/pkg/. T...
X Description: Describe the bug Project extensions (.github/extensions/) fail to load when both universal/ and platform-specific (e.g. darwin-arm64/) cache directories exist under ~/Library/Caches/copilot/pkg/. T...
Opengraph URL: https://github.com/github/copilot-cli/issues/2890
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Extensions fail to load — SEA cache directory path mismatch (universal/ vs darwin-arm64/)","articleBody":"### Describe the bug\n\nProject extensions (`.github/extensions/`) fail to load when both `universal/`\nand platform-specific (e.g. `darwin-arm64/`) cache directories exist under\n`~/Library/Caches/copilot/pkg/`. The extension subprocess immediately exits with:\n\n```\nerror: Invalid command format.\nDid you mean: copilot -i \"\u003cpath\u003e/extension_bootstrap.mjs\"?\n```\n\n### What I think is happening\n\nFrom reading the minified `index.js` in the cache, it looks like the SEA\nbinary's `import.meta.url` resolves to the **platform-specific** cache path\n(`darwin-arm64/\u003cver\u003e/`). I'll call this `Z` (the variable name in the minified\ncode).\n\nThere's a security check early in `index.js` that validates extension bootstrap\npaths against `Z`:\n\n```js\nvar Z = dirname(fileURLToPath(import.meta.url)); // → darwin-arm64/\u003cver\u003e/\nvar K = process.argv.find(e =\u003e basename(e) === \"extension_bootstrap.mjs\");\nvar Q = K ? resolve(K) : undefined;\nvar Y = Q?.startsWith(resolve(Z, \"preloads\") + sep) ? Q : undefined;\n```\n\nBut the bootstrap path that `app.js` passes to `fork()` comes from `universal/`,\nnot `darwin-arm64/`. So the prefix check fails and `Y` ends up `undefined`.\n\nIt appears that when `COPILOT_RUN_APP=1` is set (which it is, inherited from\nthe parent process), the code that loads `app.js` searches only `universal/`\nsubdirectories:\n\n```js\nlet dirs = D().map(i =\u003e join(i, \"universal\"));\nlet found = await M(\"app.js\", ...dirs);\n```\n\nOnce `app.js` loads from `universal/\u003cver\u003e/`, its own `import.meta.url` becomes\n`universal/\u003cver\u003e/app.js`, so `cliDistDir = universal/\u003cver\u003e/`. When it forks\nextensions, the bootstrap path is `universal/\u003cver\u003e/preloads/extension_bootstrap.mjs`.\nThe forked SEA child then rejects this because `Z` is still `darwin-arm64/\u003cver\u003e/`.\n\nI also noticed that `COPILOT_RUN_APP=1` appears to leak into the forked\nextension environment — the env proxy in the fork call doesn't seem to block it.\n\n\n\n### Affected version\n\nGitHub Copilot CLI 1.0.34\n\n### Steps to reproduce the behavior\n\n\n1. Install copilot (either method):\n ```bash\n npm install -g @github/copilot\n # or: brew install --cask copilot-cli\n ```\n\n2. Ensure both cache directories exist under `~/Library/Caches/copilot/pkg/`:\n ```bash\n ls ~/Library/Caches/copilot/pkg/\n # Should show both: darwin-arm64/ universal/\n ```\n\n**How these directories get created:** The SEA binary extracts its embedded JS assets to `darwin-arm64/\u003cversion\u003e/` (using the platform and architecture in the path). Separately, the auto-update mechanism in `app.js` downloads and writes new versions to `universal/\u003cversion\u003e/`.\n \nOn my machine, `darwin-arm64/1.0.34/` was created on Apr 20 at 19:54 (likely when the SEA first ran after an update), and `universal/1.0.34/` appeared on Apr 21 at 07:59 (likely from the auto-update check). Both directories contain identical file contents — the path difference is the only thing that matters.\n\nIf `universal/` doesn't exist yet, you can trigger it by running copilot a few times or waiting for an auto-update cycle. Or copy it manually:\n ```bash\n cp -r ~/Library/Caches/copilot/pkg/darwin-arm64/1.0.34 \\\n ~/Library/Caches/copilot/pkg/universal/1.0.34\n ```\n\n3. Create a minimal project extension:\n ```bash\n mkdir -p /tmp/copilot-ext-repro/.github/extensions/hello-ext\n cd /tmp/copilot-ext-repro\n git init\n ```\n\n Create `.github/extensions/hello-ext/extension.mjs`:\n ```js\n import { joinSession } from \"@github/copilot-sdk\";\n const session = await joinSession();\n session.onPreToolUse(\"*\", (event) =\u003e {\n console.error(\"[hello-ext] tool called:\", event.toolName);\n return { proceed: true };\n });\n console.error(\"[hello-ext] Extension loaded successfully\");\n ```\n\n4. Run copilot:\n ```bash\n copilot -p \"say hello\"\n ```\n\n5. Check the process log in `~/.copilot/logs/`:\n ```\n [INFO] Launching extension: .github/extensions/hello-ext/extension.mjs\n [INFO] [extension:...] error: Invalid command format.\n [INFO] [extension:...] Did you mean: copilot -i \".../universal/.../extension_bootstrap.mjs\"?\n [ERROR] Extension failed during startup: ... (code=1, signal=null)\n ```\n\n### Expected behavior\n\nThe extension should load and run. `[hello-ext] Extension loaded successfully`\nshould appear in stderr, and the `onPreToolUse` hook should fire when tools are\ncalled.\n\n\n### Additional context\n\n- **OS**: macOS (Darwin arm64)\n- **Shell**: zsh\n- **Node.js (embedded in SEA)**: v24.11.1\n\n### What I observed to confirm this\n\nI ran the SEA binary directly with each bootstrap path:\n\n```bash\nSEA=\u003cpath-to-sea-binary\u003e\n\n# Bootstrap from universal/ (what app.js currently does) → FAILS\n$ COPILOT_RUN_APP=1 $SEA ~/Library/Caches/copilot/pkg/universal/1.0.34/preloads/extension_bootstrap.mjs\n# error: Invalid command format.\n\n# Bootstrap from darwin-arm64/ (matching where Z points) → WORKS\n$ COPILOT_RUN_APP=1 $SEA ~/Library/Caches/copilot/pkg/darwin-arm64/1.0.34/preloads/extension_bootstrap.mjs\n# [extension-bootstrap] No extension path provided ← bootstrap loaded!\n```\n\nI also placed a test script in `darwin-arm64/1.0.34/preloads/` that exits with\ncode 42. Running it through the SEA binary confirmed the security check passes\nfor paths under `darwin-arm64/`. The same script placed in `universal/preloads/`\nwas rejected.\n\n### Workaround\n\nDeleting the `universal/` cache forces `app.js` to load from `darwin-arm64/`\n(matching Z), which makes the paths align:\n\n```bash\nrm -rf ~/Library/Caches/copilot/pkg/universal/\n```\n\n**Not persistent** — the `universal/` directory may be recreated by subsequent\ncopilot runs or auto-updates.","author":{"url":"https://github.com/pbleisch","@type":"Person","name":"pbleisch"},"datePublished":"2026-04-22T05:31:45.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":1},"url":"https://github.com/2890/copilot-cli/issues/2890"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:060a535b-446b-9323-06b8-d2035c92eddb |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | D336:3E643E:125318D:190ED9E:6A4DDD97 |
| html-safe-nonce | 2943d935a020b1891a022a1b0b3518536ec8d7304e07eda847ae582b46e6122c |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJEMzM2OjNFNjQzRToxMjUzMThEOjE5MEVEOUU6NkE0REREOTciLCJ2aXNpdG9yX2lkIjoiNDA5NDA2MzM3MzUxNTA4NzI1NSIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | fe4565c5cca8d3d67bc959455ba2c7300efd27a32da6c5cd82a36dcd34b8950b |
| hovercard-subject-tag | issue:4306905250 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/github/copilot-cli/2890/issue_layout |
| twitter:image | https://opengraph.githubassets.com/064dc75c2c49abff12623b709200d669c97e70357c9fb118eb36c6e893745219/github/copilot-cli/issues/2890 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/064dc75c2c49abff12623b709200d669c97e70357c9fb118eb36c6e893745219/github/copilot-cli/issues/2890 |
| og:image:alt | Describe the bug Project extensions (.github/extensions/) fail to load when both universal/ and platform-specific (e.g. darwin-arm64/) cache directories exist under ~/Library/Caches/copilot/pkg/. T... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | pbleisch |
| hostname | github.com |
| expected-hostname | github.com |
| None | 06b8a6144231bf3a234f1c2e9993861e07ce98a905912b114aa386c2d7e84b33 |
| turbo-cache-control | no-preview |
| go-import | github.com/github/copilot-cli git https://github.com/github/copilot-cli.git |
| octolytics-dimension-user_id | 9919 |
| octolytics-dimension-user_login | github |
| octolytics-dimension-repository_id | 585860664 |
| octolytics-dimension-repository_nwo | github/copilot-cli |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 585860664 |
| octolytics-dimension-repository_network_root_nwo | github/copilot-cli |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 1d344bdb7547fe6bca17a59bb2b8aac3dc9532a0 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width