Repository navigation
fix(git): surface the provider's message when linking a repository or registering a webhook fails - #660
Conversation
…ible or a webhook fails Linking a repository turned every not-found/forbidden answer of the provider into a bare ErrNotVisible, and a refused webhook registration into a fixed text, dropping messages such as "The connected project token covers only acme/ui". Keep the error codes and add the message as error_details.provider_message (redacted, control characters stripped, capped at 500 characters). ErrNotVisible/ErrAccessNotGranted are wrapped in a DetailedError, so errors.Is is unchanged. Only the settings-page webhook retry exposes it; agent-facing operations still keep provider text out of transcripts. Refs Paca-AI#656 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
When the API sends error_details.provider_message, show it as plain text under the generic message when linking or reconnecting a repository and when the webhook retry fails. Without it nothing changes. Adds the providerDetail string to all nine locales and documents the behaviour in the provider SPI guide. Refs Paca-AI#656 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Important
The webhook-retry path still hides the provider's reason for the exact errors the linked issue calls out (validation / unsupported). One functional gap to close before merge.
Reviewed changes
- Linked/reconnect errors carry provider text —
resolveThroughConnectionswrapsErrNotVisible/ErrAccessNotGrantedin a newgitdom.DetailedError, andpresenter.Erroremits its detail aserror_details.provider_messagewhile status/code andError()stay unchanged. - Redaction and cap —
gitsvc.ProviderMessagemasks credentials, drops control characters, trims, and caps at 500 runes;errors.Asstill matches the sentinel beneath the wrapper. - Webhook retry details —
RegisterWebhooknow usesfailWithProviderMessage, attaching the provider message fornot_found/forbidden/conflictonly. - Web rendering —
gitProviderMessagereads the detail; a newProviderDetailrenders it as plain text under the generic message in the link, reconnect and webhook-retry alerts; the new locale string exists in all 9 locales. - Docs —
git-provider-spi.mddocuments that these messages are user-visible (redacted, capped 500).
I ran go test for internal/service/git, internal/transport/http/presenter, and internal/transport/http/handler — all pass.
ℹ️ The webhook notice still asserts the likely cause
project.git.webhook.description still says the token "probably lacks the permission to manage webhooks" and is rendered unconditionally above the failure alert. Now that the real reason is shown underneath, a panel for a validation/unsupported failure contradicts itself: the static copy names a permission problem while the detail says, for example, that the instance has no public URL. Since the PR's stated "Why" is that this fixed string is misleading, consider softening it (or replacing it when a provider detail is present) so the notice does not lead with a cause the provider just refuted.
Technical details
# Soften the fixed webhook copy when a provider detail is shown
## Affected sites
- apps/web/src/i18n/locales/*/projects.json — `project.git.webhook.description` still states the token likely lacks permission.
- apps/web/src/components/projects/settings/GitSettings.tsx:700-715 — description is rendered before the `{failed ? ...}` block, so it is always visible.
## Required outcome
- The webhook notice must not assert a permission cause when a provider detail explaining a different cause is displayed.
## Suggested approach (optional)
- Reword the description to a neutral "Paca couldn't register a webhook on this repository" and keep the actionable permission hint as secondary text, or hide the hint once `detail` is non-null.deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
A webhook retry attached the provider's message only for not_found, forbidden and conflict. Validation and unsupported answers (an unreachable URL, an instance without a public URL) are exactly where the fixed text is wrong, so attach it for every provider code except internal, which stays opaque. Refs Paca-AI#656 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The notice always said the token probably lacked the permission to manage webhooks, even when the provider's message right below named another cause. The description is now neutral, and the permission hint shows only until the platform has given its own reason. Refs Paca-AI#656 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Addressed the review in 0656c7b and 035cdf3:
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Both commits since the prior review close the two comments from that pass: the webhook-retry path now carries the provider's message for every code, and the notice no longer asserts a permission cause once the platform has said otherwise. Nothing new to flag.
- Every provider code surfaces its message —
failWithProviderMessagenow attacheserror_details.provider_messagefor all provider codes exceptinternal, instead of onlynot_found/forbidden/conflict, sovalidationandunsupportedwebhook failures (ensureWebhook's "no public URL…", an unreachable URL) reach the UI; handler tests cover both. - Neutral webhook copy with a conditional hint —
project.git.webhook.descriptionbecame the neutral "Paca couldn't register a webhook…", and the actionable permission guidance moved to a newwebhook.permissionHintstring rendered only when no provider detail is present (failed && detail ? null : hint); locale parity holds across all 9 files. - Docs —
git-provider-spi.mdnow states any failedEnsureWebhookretry (any code exceptinternal) is shown to the user.
I ran go test for internal/service/git, internal/transport/http/presenter, and internal/transport/http/handler — all pass. biome check is clean on the changed web files (web vitest isn't runnable in this environment — no node_modules).
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
Repositories listed for a project now carry webhook_attempted, true when the audit log holds a webhook.register entry for them. One aggregated query per listing, no new migration. Refs Paca-AI#656. Repos carried over by the plugin migrations (Paca-AI/paca-plugin-gitlab#6) arrive without a webhook and without Paca ever having tried to register one. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
When webhook_attempted is false the notice no longer says Paca couldn't register the webhook or suggests a permissions problem: it explains that none was registered yet and offers "Register webhook". After a failed try it falls back to the usual wording. New strings in all nine locales. Refs Paca-AI#656. Repos carried over by the plugin migrations (Paca-AI/paca-plugin-gitlab#6) are in this state. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Added 2ff41a7 and 443b551. Repositories carried over by the plugin migrations (Paca-AI/paca-plugin-gitlab#6) arrive without a webhook that Paca has ever tried to register, and the notice blamed token permissions for them. |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
The two commits since the prior review make the webhook notice tell "never tried" apart from "tried and failed":
webhook_attemptedflows end to end —ListRepositoriespopulatesgitdom.Repository.WebhookAttemptedfrom a newWebhookAttemptsstore method that readsgit_action_log(tool = 'webhook.register'), surfaced aswebhook_attemptedonGitRepositoryResponseand consumed by the webGitRepositorytype.repository_idisON DELETE SET NULL, so an unlinked repository drops out of the set.- The notice no longer guesses at a cause it never observed —
WebhookNoticecomputesneverTried = repository.webhook_attempted === false && !failed, switching the description towebhook.descriptionNotTriedand the button towebhook.register, and hiding the permission hint until an attempt has actually been made. NewdescriptionNotTried/registerkeys exist in all 9 locales; an unset field (older API) falls back to the failure wording, which is the safe default. - Tests — a postgres store test for the audit-log query, service tests for the listing distinction, and web tests for both the never-attempted and attempted-and-failed wordings.
go build ./... and go test for internal/service/git, internal/transport/http/handler, internal/transport/http/presenter, and internal/repository/postgres all pass. Web vitest is not runnable here (no node_modules).
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
pikann
left a comment
There was a problem hiding this comment.
LGTM! Please resolve the merge conflicts before merging.
Thank you for your contribution! 🚀
Resolve conflicts with the repository description (Paca-AI#658) and the task-ref parser fix (Paca-AI#657): both sides added to the same places. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
The only commit since the prior review is the merge of upstream/master (b78bbeb7), which resolves the conflicts the human reviewer flagged. It folds four upstream PRs into the branch without disturbing the provider-message feature:
- Repository descriptions now coexist with provider messages —
GitSettings.tsxcarries both the upstream link-form description field /RepositoryDescriptionblock and this PR'sProviderDetail/WebhookNotice;GitRepositoryResponsemaps bothDescriptionandWebhookAttempted; theStoreport keepsWebhookAttemptsbeside the newSetRepositoryDescription. - Other upstream commits merged in — the task-ref parser fix (#657), the change-request state fix for edited/synchronized events (#659), and the ACP cancel change (#655) land in
service.goalongside the untouchedresolveThroughConnectionswrapping (ProviderMessage→WithDetail). - Tests and locales merged without loss —
service_test.gokeepsTestLinkRepository_ProviderMessageRidesAlongNotVisible/TestProviderMessagenext to upstream'sTestFindTaskRefs; the en locale carries both the provider-detail strings and the description strings.
go build ./... and go test for internal/service/git, internal/transport/http/{dto,handler,presenter,router,middleware}, and internal/repository/postgres all pass; go vet compiles the changed packages' tests cleanly. Web vitest is not runnable here (no node_modules).
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

Summary
When a Git provider plugin explains why it refused something ("The connected project token covers only acme/ui. Add a connection for acme/api..."), Paca now shows that explanation under its generic error in project Git settings. Error codes and behaviour without a provider message are unchanged.
Why
resolveThroughConnectionsturned every not-found/forbidden answer into a bareErrNotVisible/ErrAccessNotGranted, dropping the provider'sMessage.Changes
gitdom.DetailedErrorwraps a sentinel with the provider's text (errors.Isstill matches;Error()unchanged).gitsvc.ProviderMessageredacts secrets, strips control characters, caps at 500 characters. The presenter exposes it aserror_details.provider_message. The webhook retry endpoint adds it to the not-found/forbidden/conflict mappings. Agent-facing git operations are untouched (provider text still stays out of transcripts).gitProviderMessage()reads the detail;GitSettingsshows it as plain text (never HTML) in the link, reconnect and webhook-retry errors. Newproject.git.errors.providerDetailstring in all 9 locales.docs/plugins/git-provider-spi.mdnotes that these messages are shown to the user.Tests
errors.Is(err, ErrNotVisible)still true; no message leaves the bare sentinel/noerror_details; redaction and cap; presenter and handler mapping.gitProviderMessageunit tests; locale parity.go vet,go test ./...,tsc -b,biome checkpass.Refs #656
🤖 Generated with Claude Code