Skip to content

fix(ui): show a not-found state instead of a blank page for an unknown agent (#1914) - #1915

Merged
vybe merged 1 commit into
devfrom
feature/1914-agent-not-found-state
Aug 4, 2026
Merged

fix(ui): show a not-found state instead of a blank page for an unknown agent (#1914)#1915
vybe merged 1 commit into
devfrom
feature/1914-agent-not-found-state

Conversation

@obasilakis

Copy link
Copy Markdown
Contributor

Closes #1914

The bug

Navigating to /agents/<unknown> rendered a completely blank page — no error, no way back. AgentDetail.vue already had an error banner (v-if="error && !agent"). It just never fired.

Root cause — the store, not the view

stores/agents.js::fetchAgent caught the 404, logged it to the console, and returned undefined:

} catch (error) {
  this.error = error.message
  console.error('Failed to fetch agent:', error)   // <-- swallowed, returns undefined
}

So AgentDetail::loadAgent's catch never ran: error stayed '' and agent stayed null, which made both v-if="error && !agent" and v-if="agent" false. Nothing rendered. The only evidence was a console line the user never sees.

Returning undefined on failure is indistinguishable from a successful empty fetch — that ambiguity is the whole bug.

The fix

  • fetchAgent re-throws. The one caller that genuinely tolerates a failure — the post-stop status poll in AgentDetail (waitForAgentStatus, ~L739) — already wraps it in its own try/catch, so it keeps polling through transient errors exactly as before. Verified: only two callers of agentsStore.fetchAgent exist, both in this file. (AgentWorkspace.vue / AgentBrainOrb.vue have their own local fetchAgent that hits axios directly and deliberately degrades — untouched.)
  • loadAgent splits 404 from everything else. 404 → a not-found panel. Anything else (network blip, 500, expired token) → the generic banner, now with a Retry. Retrying a 404 is pointless, so only the retryable branch offers one.
  • The exit is the Dashboard, not the Agents list — the agent you asked for isn't in that list either, so the list is a second dead end for the same question.

The copy is deliberately vague, and a test pins that

The backend returns a uniform 404 for "no such agent" and "you can't see this agent" on purpose — the differential is an enumeration oracle (Invariant #8 / #186). So the panel says:

skills-fixture doesn't exist, or you don't have access to it.

…covering both without claiming which. One of the specs asserts the absence of an existence claim (not.toContainText(/no such agent|does not exist on this instance|was deleted/i)) so a well-meaning future copy edit can't quietly leak it back.

Tests

src/frontend/e2e/agent-not-found.spec.js — 3 specs, tagged @smoke so they run in CI on ui-labelled PRs. They're @smoke-eligible precisely because the agent deliberately does not exist: unlike every other AgentDetail spec, they need no fixture agent, no running container, and no Claude dispatch. ~2s, deterministic.

  1. unknown agent renders the not-found state (not a blank page) + "Back to Dashboard" navigates
  2. the copy doesn't disclose whether the agent exists
  3. a non-404 (mocked 500) shows the retryable load error, not the not-found state

Verified failing on the pre-fix code — stashed the two source files, re-ran, 3/3 failed; restored, 3/3 pass. Existing smoke.spec.js still green. Happy path (real agent loads) confirmed unaffected by the re-throw.

Verification

Run against a live stack via a Vite dev server on the branch, proxying to the local backend:

State Result
/agents/skills-fixture "Agent not found" panel + Back to Dashboard
Click Back to Dashboard lands on /
/agents/acme-scout normal agent page, unchanged
mocked 500 red banner + Retry
dark + light both styled

Scope note

stores/agents.js has the same swallow-and-return-undefined shape in other actions (fetchAgents, etc.). Left alone here — none of them produce a blank page, and widening this would turn a targeted bug fix into a store-wide refactor. Worth a follow-up.

…n agent (#1914)

Navigating to /agents/<unknown> rendered nothing at all — no error, no way
back. AgentDetail already HAD an error banner; it just never fired.

Root cause: stores/agents.js::fetchAgent caught the 404, logged it to the
console, and returned `undefined`. So AgentDetail::loadAgent's catch never
ran — `error` stayed '' and `agent` stayed null, making BOTH `v-if="error &&
!agent"` and `v-if="agent"` false. The only evidence was a console line the
user never sees.

- fetchAgent re-throws. Returning `undefined` on failure is indistinguishable
  from a successful empty fetch, which is what made the failure invisible. The
  one caller that genuinely tolerates a failure (the post-stop status poll)
  already wraps it in its own try/catch, so it is unaffected.
- loadAgent splits 404 from everything else: 404 -> a not-found panel with a
  "Back to Dashboard" link (no dead empty state); anything else -> the generic
  banner, now with a Retry (retrying a 404 is pointless, so only the
  retryable branch offers it). The exit is the Dashboard, not the Agents
  list — the agent you asked for isn't in that list either, so the list is a
  second dead end for the same question.
- The copy does NOT say whether the agent exists. The backend returns a
  uniform 404 for "no such agent" and "you can't see this agent" on purpose —
  the differential is an enumeration oracle (Invariant #8 / #186) — and the
  UI must not reintroduce it. A test pins the absence of an existence claim.

e2e/agent-not-found.spec.js is @smoke: the agent deliberately does not exist,
so unlike the other AgentDetail specs it needs no fixture agent, no running
container and no Claude dispatch. Verified failing on the pre-fix code (3/3)
and passing after.
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

⚠️ Nightly unit-suite check skipped — merge conflict against dev.

Resolve by running git merge dev locally and pushing the result. The next nightly run will re-test once the conflict is gone.

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validated via /validate-pr — clean pass: root cause fixed at the store, both fetchAgent call-sites audited, enumeration-safe 404 copy pinned by test, regression specs verified failing pre-fix and green in CI (e2e ran). Approving and merging in sequence.

@vybe
vybe merged commit b920282 into dev Aug 4, 2026
27 of 29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants