# GitHub Copilot app: private plugin marketplaces are unusable, and marketpla...
- Ngôn ngữ chính
- Không có dữ liệu ngôn ngữ
- Star
- 2.1k
- Fork
- 153
- Chỉ số merge pull request
- Không có pull request nào được merge trong 30 ngày
Mô tả
# GitHub Copilot app: private plugin marketplaces are unusable, and marketplace refresh fails on read-only git files
**Component:** GitHub Copilot desktop app (`github.exe`) — plugin store / marketplace fetch
**Version:** app build with Copilot CLI 1.0.79, bundled git 2.53.0-3, bundled gh 2.96.0 · Windows
**Reported symptom:** "access denied" when installing or updating plugins
Two independent defects produce that one symptom. They are unrelated and can be fixed separately.
---
## Bug 1 — A private marketplace is always fetched with the *default* account
### Summary
Marketplaces declared in `~/.copilot/settings.json` under `extraKnownMarketplaces` never get a row in
the app's `plugin_marketplace_accounts` table, so the marketplace clone silently falls back to the
**default** GitHub account. If that account cannot see the repository, the marketplace can never be
installed or updated, and there is no way for the user to fix it.
### Environment
Two GitHub accounts are signed in:
| account | `is_default` | can read `agency-microsoft/.github-private` |
| --- | --- | --- |
| `AsafMah` | 1 | no — HTTP 404 |
| `asafmahlev_microsoft` | 0 | yes — `private=true` |
The marketplace is declared in `~/.copilot/settings.json`:
```json
"extraKnownMarketplaces": {
"agency": { "source": { "source": "github", "repo": "agency-microsoft/.github-private" } }
}
```
### Evidence
Installing any plugin from that marketplace fails. From the app log:
```
09:56:29.281 received message kind="install_plugin" text={"type":"install_plugin","source":"a11y@agency"}
09:56:30.098 github_app::git::trampoline: trampoline accepted connection
09:56:30.099 github_app::git::trampoline: trampoline credential helper invoked verb="get"
09:56:30.099 github_app::git::trampoline: trampoline credential helper resolved token host="github.com"
09:56:30.647 ERROR ... failed to install plugin error=RPC error -32603: Request plugins.install failed
with message: Failed to fetch marketplace: Failed to fetch GitHub marketplace
agency-microsoft/.github-private: Command failed: git clone --depth 1 --progress
https://github.com/agency-microsoft/.github-private.git
C:\Users\...\AppData\Local\copilot\marketplaces\agency-microsoft-.github-private
Cloning into '...'...
remote: Repository not found.
fatal: Authentication failed for 'https://github.com/agency-microsoft/.github-private.git/'
```
The trampoline resolves a token *by host only* and returns the default account's token, which 404s on
that private repo.
### The mechanism already exists but is never populated
`data.db` contains exactly the right table, and the app queries it:
```sql
CREATE TABLE plugin_marketplace_accounts (
marketplace_name TEXT PRIMARY KEY NOT NULL,
account_id TEXT NOT NULL,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
);
SELECT account_id FROM plugin_marketplace_accounts WHERE marketplace_name = ?1
INSERT INTO plugin_marketplace_accounts (marketplace_name, account_id) VALUES (?1, ?2)
ON CONFLICT(marketplace_name) DO UPDATE SET account_id = excluded.account_id
```
The RPC surface includes `add_plugin_marketplace`, **`authenticate_plugin_marketplace`**,
`remove_plugin_marketplace`, `browse_plugin_marketplace`, `refresh_plugin_marketplaces`.
The feature is enabled for this user — telemetry reports
`"features.private_marketplace_authentication":"true"`.
Despite that:
- `SELECT * FROM plugin_marketplace_accounts` returns **zero rows**.
- No `add_plugin_marketplace` or `authenticate_plugin_marketplace` call appears anywhere in the
retained app logs — the marketplace was declared directly in `settings.json` (by the Agency
installer), so it never passed through the flow that records an account.
- The plugin store UI offers **no** authenticate / sign-in affordance for this marketplace.
### Impact
Any user whose default Copilot account differs from the account that can read a private marketplace
is permanently blocked from installing or updating its plugins, with a misleading "access denied"
message and no path to resolve it. Changing the default account is not a workaround — projects are
bound to explicit accounts and other repositories depend on the current default.
### Repro
1. Sign in with two GitHub accounts; make the one *without* access to a private repo the default.
2. Add that private repo as a marketplace via `~/.copilot/settings.json` `extraKnownMarketplaces`
(i.e. the CLI path, not the app's "add marketplace" flow).
3. Try to install any plugin from it → `Repository not found` / `Authentication failed`.
### Suggested fix
1. Surface `authenticate_plugin_marketplace` in the plugin store for **any** marketplace, including
those declared in `settings.json` — ideally offered automatically when a fetch fails with 404/403
and more than one account is signed in.
2. When resolving a marketplace fetch, consult `plugin_marketplace_accounts` and fall back to the
default account only when no binding exists (this may already be the intent; the row is simply
never created for CLI-declared marketplaces).
3. Report the underlying auth failure distinctly from a filesystem permission error — see Bug 2, which
surfaces under the identical "access denied" wording.
### Note
Inserting the row manually while the app was running had no effect — the same install still resolved
the default account. Whether the app reads this table only at startup was not verified (no restart
was performed).
---
## Bug 2 — Marketplace refresh fails with `Access is denied. (os error 5)` on read-only git pack files
### Summary
Installing a plugin triggers a marketplace refresh that recursively deletes the cached clone. Git
marks pack files (`.pack`, `.idx`, `.rev`) **read-only**, and a recursive delete that does not clear
the read-only attribute fails on Windows with `ERROR_ACCESS_DENIED (5)`. This affects **every**
marketplace, public ones included, and is unrelated to authentication.
### Evidence
```
09:05:07.633 received message kind="install_plugin"
text={"type":"install_plugin","source":"csharp-dotnet-development@awesome-copilot"}
09:05:15.436 ERROR ... failed to install plugin error=RPC error -32603: Request plugins.install failed
with message: Failed to install plugin: Access is denied. (os error 5)
source=csharp-dotnet-development@awesome-copilot
```
The marketplace cache contained read-only files, all of them git pack data:
```
marketplaces\github-awesome-copilot\.git\objects\pack\pack-4f4ed23f….idx (read-only)
marketplaces\github-awesome-copilot\.git\objects\pack\pack-4f4ed23f….pack (read-only)
marketplaces\github-awesome-copilot\.git\objects\pack\pack-4f4ed23f….rev (read-only)
marketplaces\github-awesome-copilot\.git\objects\pack\pack-803651e3….idx (read-only)
marketplaces\github-awesome-copilot\.git\objects\pack\pack-803651e3….pack (read-only)
marketplaces\github-awesome-copilot\.git\objects\pack\pack-803651e3….rev (read-only)
```
71 read-only files existed across all marketplace caches.
### Mechanism confirmed in isolation
```python
sub = /objects/pack ; write /objects/pack/x.pack ; os.chmod(f, stat.S_IREAD)
shutil.rmtree() # same syscall pattern as Rust std::fs::remove_dir_all
-> PermissionError winerror 5 "Access is denied"
os.chmod(f, stat.S_IWRITE) ; shutil.rmtree() -> succeeds
```
Clearing the read-only attribute on those 71 files made the deletion succeed.
### Impact
Plugin install/update breaks for any marketplace whose cache has been fetched at least once — git
creates read-only packs on clone and on every subsequent fetch, so this recurs continuously and
resolves itself only by chance. The error message ("access denied") strongly implies a permissions or
authentication problem, sending users down the wrong path entirely.
### Repro
1. Let the app clone any marketplace (creates read-only `.pack`/`.idx`/`.rev` files).
2. Install a plugin from it, triggering the refresh/delete.
3. → `Failed to install plugin: Access is denied. (os error 5)`.
### Suggested fix
Clear the read-only attribute before recursive deletion on Windows — the standard remedy is a
`remove_dir_all` that retries after `set_readonly(false)` on `ERROR_ACCESS_DENIED` (e.g. the
`remove_dir_all` crate), rather than `std::fs::remove_dir_all` directly. Apply it everywhere the app
deletes a git working copy, not just marketplace caches.
---
## Related observation (not investigated)
The app log shows a repeating ~12 s cycle in `resources_subscription_loop`:
```
github_app::git::remote: Both local and token auth failed label=…
github_app::git: git command exited with non-zero status
```
This looks like a background repo poll retrying failed auth indefinitely. Possibly the same
default-account root cause as Bug 1, but it was not investigated.
---
| Field | Value |
| --- | --- |
| App version | 1.1.7 |
| OS | Windows 10.0.26200 |
| Theme | GitHub |
| Path | /chat |
| Tenure | Week 6 |
Hướng dẫn đóng góp
Hướng nghiên cứu
Bắt đầu từ các điểm vào RPC của plugin marketplace, đặc biệt là authenticate_plugin_marketplace, refresh_plugin_marketplaces và đường dẫn extraKnownMarketplaces trong settings.json. Tiếp theo, theo dõi việc tra cứu tài khoản marketplace trong data.db, sau đó kiểm tra luồng xóa cache đối với các tệp Git pack chỉ đọc trên Windows. Hoàn tất khi các marketplace riêng tư có thể liên kết với tài khoản dự kiến và việc làm mới/cài đặt marketplace thành công mà không gặp lỗi access-denied.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- git, github, json
- Lĩnh vực
- authentication, databases, desktop, devtools
- Loại issue
- Lỗi
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức độ hoạt động
- Ít trao đổi
- Độ rõ ràng
- Khá rõ ràng
- Mức phù hợp với người mới
- 35/100