fix(canvas): Secrets "Test" reports honest error instead of fake "Connection timed out" #1424

Merged
devops-engineer merged 1 commits from fix/secret-test-honest-error-internal-492 into staging 2026-05-17 17:00:07 +00:00
Owner

Summary

Canvas Settings -> Secrets: the Test action always rendered "Test failed - Connection timed out. Service may be down." even on a fast failure. Root cause: validateSecret() POSTs to ${PLATFORM_URL}/secrets/validate, a route that was never implemented on the workspace-server router (router.go registers /secrets, /secrets/values, /settings/secrets, /admin/secrets only) nor on the Next.js canvas. Live probe: POST /secrets/validate -> HTTP 404 in 0.28s (a fast 404, not a timeout). request() throws ApiError(404); TestConnectionButton's bare catch {} swallowed it and printed a hardcoded fake-timeout string for every failure mode.

Tracking: internal#491. Same "make the dead affordance honest" approach as the reveal control (internal#490 / PR#1421).

Change (minimal, surgical)

canvas/src/components/ui/TestConnectionButton.tsx only:

  • Bind the caught error; import ApiError.
  • ApiError 404/501 -> "Key validation is not available for this service yet. The key was not tested." (the real situation)
  • ApiError other status -> "Could not verify key (server returned N). Saving is unaffected."
  • Non-ApiError throw (genuine network/abort) -> "Could not reach the validation service. Check your connection and try again."

No speculative server-side /secrets/validate endpoint (would pull in provider HTTP clients/egress/per-provider auth — out of minimal-RC scope).

Practical impact

  • Save is NOT gated on TestcreateSecret/updateSecret PUT /settings/secrets independently. Tokens save and are usable; the message was cosmetic-but-misleading, not a functional block.
  • "+ Add API Key -> ANTHROPIC_API_KEY" uses the same Test path and is equally non-blocking (Save independent of Test).

Test plan

  • canvas/src/components/ui/__tests__/TestConnectionButton.test.tsx — added 404-regression + non-404 + connectivity cases
  • canvas/src/components/__tests__/TestConnectionButton.test.tsx — updated stale /timed out/ assertion + added 404-regression case
  • Fail-before / pass-after proven (stash component, 5 new tests fail with old fake string; restored -> 32/32 pass)
  • npx vitest run both suites on staging base: 32 passed; no TS errors in changed files

Scope

Touched ONLY TestConnectionButton.tsx + its 2 test files. Did NOT touch scope-guarded files (claude_sdk_executor.py, executor_helpers.py, activity.go, useChatSocket.ts, SecretRow.tsx, RevealToggle.tsx). No bypass / no self-approve / no admin-merge — orchestrator owns review + merge.

Closes internal#491

## Summary Canvas Settings -> Secrets: the **Test** action always rendered **"Test failed - Connection timed out. Service may be down."** even on a fast failure. Root cause: `validateSecret()` POSTs to `${PLATFORM_URL}/secrets/validate`, a route that **was never implemented** on the workspace-server router (`router.go` registers `/secrets`, `/secrets/values`, `/settings/secrets`, `/admin/secrets` only) nor on the Next.js canvas. Live probe: `POST /secrets/validate` -> **HTTP 404 in 0.28s** (a fast 404, not a timeout). `request()` throws `ApiError(404)`; `TestConnectionButton`'s bare `catch {}` swallowed it and printed a hardcoded fake-timeout string for **every** failure mode. Tracking: internal#491. Same "make the dead affordance honest" approach as the reveal control (internal#490 / PR#1421). ## Change (minimal, surgical) `canvas/src/components/ui/TestConnectionButton.tsx` only: - Bind the caught error; import `ApiError`. - `ApiError` 404/501 -> "Key validation is not available for this service yet. The key was not tested." (the real situation) - `ApiError` other status -> "Could not verify key (server returned N). Saving is unaffected." - Non-`ApiError` throw (genuine network/abort) -> "Could not reach the validation service. Check your connection and try again." No speculative server-side `/secrets/validate` endpoint (would pull in provider HTTP clients/egress/per-provider auth — out of minimal-RC scope). ## Practical impact - **Save is NOT gated on Test** — `createSecret`/`updateSecret` PUT `/settings/secrets` independently. Tokens save and are usable; the message was cosmetic-but-misleading, not a functional block. - **"+ Add API Key -> ANTHROPIC_API_KEY" uses the same Test path** and is **equally non-blocking** (Save independent of Test). ## Test plan - [x] `canvas/src/components/ui/__tests__/TestConnectionButton.test.tsx` — added 404-regression + non-404 + connectivity cases - [x] `canvas/src/components/__tests__/TestConnectionButton.test.tsx` — updated stale `/timed out/` assertion + added 404-regression case - [x] Fail-before / pass-after proven (stash component, 5 new tests fail with old fake string; restored -> 32/32 pass) - [x] `npx vitest run` both suites on `staging` base: **32 passed**; no TS errors in changed files ## Scope Touched ONLY `TestConnectionButton.tsx` + its 2 test files. Did NOT touch scope-guarded files (claude_sdk_executor.py, executor_helpers.py, activity.go, useChatSocket.ts, SecretRow.tsx, RevealToggle.tsx). No bypass / no self-approve / no admin-merge — orchestrator owns review + merge. Closes internal#491
hongming added 1 commit 2026-05-17 14:39:17 +00:00
fix(canvas): Secrets "Test" reports honest error instead of fake "timed out"
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 3s
CI / Detect changes (pull_request) Successful in 5s
E2E API Smoke Test / detect-changes (pull_request) Successful in 8s
E2E Chat / detect-changes (pull_request) Successful in 6s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 6s
Harness Replays / detect-changes (pull_request) Successful in 7s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m2s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 9s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
gate-check-v3 / gate-check (pull_request) Successful in 6s
qa-review / approved (pull_request) Successful in 5s
security-review / approved (pull_request) Successful in 7s
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request) Successful in 5s
sop-tier-check / tier-check (pull_request) Successful in 6s
CI / Platform (Go) (pull_request) Successful in 6m12s
CI / Canvas (Next.js) (pull_request) Successful in 8m16s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
CI / Python Lint & Test (pull_request) Successful in 2s
E2E Chat / E2E Chat (pull_request) Failing after 1s
Harness Replays / Harness Replays (pull_request) Successful in 2s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 1s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 50s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 1m39s
CI / all-required (pull_request) Successful in 1s
audit-force-merge / audit (pull_request) Successful in 5s
3fc585b939
The Secrets test button calls POST ${PLATFORM_URL}/secrets/validate, a
route that has never been implemented on the workspace-server router
(router.go registers /secrets, /secrets/values, /settings/secrets,
/admin/secrets — no /secrets/validate) nor on the Next.js canvas. Live
probe: POST /secrets/validate → HTTP 404 in 0.28s (a fast 404, not a
network timeout).

request() throws ApiError(404); TestConnectionButton's bare `catch {}`
swallowed it and unconditionally rendered the hardcoded string
"Connection timed out. Service may be down." — factually wrong and
indistinguishable from a real outage or a token rejection.

Minimal fix (same "make the dead affordance honest" approach as the
reveal control, internal#490 / PR#1421): bind the caught error and
surface the real failure — distinguish "validation not available"
(404/501), a non-404 server error (with status), and a genuine
connectivity failure. No speculative server-side validate endpoint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Member

[core-qa-agent] N/A — Canvas-only: TestConnectionButton honest error instead of fake timeout + TestConnectionButton.test.tsx. Pure Canvas UI.

[core-qa-agent] N/A — Canvas-only: TestConnectionButton honest error instead of fake timeout + TestConnectionButton.test.tsx. Pure Canvas UI.
core-fe approved these changes 2026-05-17 14:50:51 +00:00
core-fe left a comment
Member

core-fe review

APPROVE — accurate error reporting.

What this changes

TestConnectionButton.tsx was showing "Connection timed out" when the validation API returned a 404. 404 is not a timeout — it's a "not found". The fix adds an ApiError class to distinguish error types and shows honest messages:

  • 404 → "secret not found" / "could not reach validation service" (honest)
  • Network error → "could not reach the validation service" (honest)
  • Timeout → "Connection timed out" (accurate)

Test coverage

Two new test cases covering the honest error paths.

Consistency with #1421

Same theme as PR #1421 — honest error messages instead of misleading ones. Both by hongming.

## core-fe review APPROVE — accurate error reporting. ### What this changes TestConnectionButton.tsx was showing "Connection timed out" when the validation API returned a 404. 404 is not a timeout — it's a "not found". The fix adds an `ApiError` class to distinguish error types and shows honest messages: - 404 → "secret not found" / "could not reach validation service" (honest) - Network error → "could not reach the validation service" (honest) - Timeout → "Connection timed out" (accurate) ### Test coverage Two new test cases covering the honest error paths. ✅ ### Consistency with #1421 Same theme as PR #1421 — honest error messages instead of misleading ones. Both by hongming.
Member

/sop-ack comprehensive-testing Canvas Vitest + TestConnectionButton.test.tsx updated with honest error coverage.

/sop-ack comprehensive-testing Canvas Vitest + TestConnectionButton.test.tsx updated with honest error coverage.
Member

/sop-ack five-axis-review UX fix: TestConnectionButton shows accurate error messages (404 = not found, not timeout). ApiError class distinguishes error types.

/sop-ack five-axis-review UX fix: TestConnectionButton shows accurate error messages (404 = not found, not timeout). ApiError class distinguishes error types.
Member

/sop-ack memory-consulted PLAN.md — no canvas accessibility issues in memory related to this change.

/sop-ack memory-consulted PLAN.md — no canvas accessibility issues in memory related to this change.
Member

/sop-ack no-backwards-compat Error message text changes on TestConnectionButton — no API surface change.

/sop-ack no-backwards-compat Error message text changes on TestConnectionButton — no API surface change.
Member

/sop-ack local-postgres-e2e Canvas Vitest 210 files, 3293 tests pass. Pure canvas UI change.

/sop-ack local-postgres-e2e Canvas Vitest 210 files, 3293 tests pass. Pure canvas UI change.
Member

/sop-ack staging-smoke Canvas Vitest 210 files, 3293 tests pass. Pure canvas UI change — no backend surface.

/sop-ack staging-smoke Canvas Vitest 210 files, 3293 tests pass. Pure canvas UI change — no backend surface.
Member

[core-security-agent] APPROVED — OWASP X/X clean; error handling (ApiError) adds no new surface, TestConnectionButton UI-only

[core-security-agent] APPROVED — OWASP X/X clean; error handling (ApiError) adds no new surface, TestConnectionButton UI-only
core-uiux approved these changes 2026-05-17 16:59:35 +00:00
core-uiux left a comment
Member

Five-axis (UX/sec): catch-path discriminates ApiError 404/501 vs other status vs network; no behavior change beyond honest error string; no secret/key material in any message; tests cover 404/500/network. Clean.

Five-axis (UX/sec): catch-path discriminates ApiError 404/501 vs other status vs network; no behavior change beyond honest error string; no secret/key material in any message; tests cover 404/500/network. Clean.
core-qa approved these changes 2026-05-17 16:59:36 +00:00
core-qa left a comment
Member

Five-axis (QA): both test files updated, mock ApiError matches real shape, regression cases for internal#492 (404 not-available, 500 status, genuine network). state/onResult/reset-timer unchanged. Clean.

Five-axis (QA): both test files updated, mock ApiError matches real shape, regression cases for internal#492 (404 not-available, 500 status, genuine network). state/onResult/reset-timer unchanged. Clean.
devops-engineer merged commit 0655d5acf0 into staging 2026-05-17 17:00:07 +00:00
devops-engineer deleted branch fix/secret-test-honest-error-internal-492 2026-05-17 17:00:07 +00:00
Sign in to join this conversation.
5 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#1424