Skip to content

bug(kanban): "Kanban unavailable: not found" after WebUI update — stale SW bundle calls obsolete endpoint shape #1823

@nesquena-hermes

Description

@nesquena-hermes

Summary

WebUI's Kanban panel renders "Kanban unavailable: not found" in both the sidebar and the main board area, even when the user has live kanban data on disk and is using hermes kanban outside the WebUI successfully.

Reported in the Nous Research Discord by user "Ma":

"The latest one have some sync problem with Kanban (don't know if anyone report it yet) 😄 ... Yes I am reporting it because I have been using kanban before opening the web ui."

Root cause confirmed

I reproduced two distinct paths to the exact Kanban unavailable: not found error string:

Repro 1 — subpath mount mismatch

$ curl -i http://127.0.0.1:8787/hermes/api/kanban/config
HTTP/1.0 404 Not Found
{"error": "not found"}

Repro 2 — trailing-slash intolerance

$ curl -i http://127.0.0.1:8787/api/kanban/config/
HTTP/1.0 404 Not Found
{"error": "not found"}

Both produce the identical 404 + bare "not found" body that static/panels.js:1233 then prefixes with kanban_unavailable → screenshot's "Kanban unavailable: not found".

Why this specific text

api/kanban_bridge.py:handle_kanban_get does literal-equality matching on every path (if path == "/api/kanban/config":). Any normalization difference — trailing slash, subpath prefix, case difference — falls through return False at line 1099. The False propagates up to server.py:133's do_GET handler which emits:

if result is False:
    return j(self, {'error': 'not found'}, status=404)

That's the exact bytes the JS receives. The static/panels.js:1233 loadKanban() catch block then renders it as "Kanban unavailable: not found".

Why kanban specifically (not sessions/profiles/etc)

Other endpoints have the same bug class — curl /hermes/api/sessions also 404s — but their failures don't render as user-visible panels. Kanban is the loudest because:

  1. The error text is rendered into the main panel container (#kanbanBoard) AND the sidebar list container (#kanbanList) — doubled visibility (panels.js:1234-1235)
  2. Other endpoint failures are absorbed by silent try/catch blocks elsewhere in the JS
  3. The user has a clear expectation ("I have kanban data, where is it?") that the error message doesn't address

Hypotheses for Ma's specific case

In rough probability order:

A. Trailing-slash via reverse proxy (most likely)

Ma is behind an Nginx / Traefik / Caddy that normalizes their request to /api/kanban/config/ before forwarding. The WebUI's literal path match misses → 404. Common in self-hosted deployments where the proxy was configured for a different app. Verify: /api/sessions would also 404 if so — but they wouldn't notice because that endpoint's failure doesn't render visibly.

B. Subpath mount with broken stripping

Ma loads WebUI from /hermes/ or similar. The frontend api() wrapper builds requests relative to baseURI → server sees /hermes/api/kanban/config → no kanban handler matches that prefix → 404. Verify: rest of WebUI would be broken too (sessions, profiles, etc), so this would be a louder report.

C. Service-worker stale bundle (less likely now)

Old panels.js calling endpoints that have moved or had their shape changed. The May 4-5 PR cluster (#1660 multi-board, #1665 stats) did add new endpoints but didn't rename existing ones. Verify: would only affect users with stale SW; clears with hard-refresh.

D. AttributeError from older hermes_cli

Older hermes_cli lacking board_exists would raise AttributeError, NOT caught in handle_kanban_get (only ImportError/LookupError/ValueError/RuntimeError are). Would propagate to do_GET's blanket except → returns 500, not 404. Verify: would show different error text. Doesn't match screenshot.

Fix tiers

Tier 1 — robust path matching in kanban dispatcher (M1, ~30 LOC)

In api/kanban_bridge.py:handle_kanban_get, normalize the path before matching:

def _normalize_kanban_path(parsed):
    """Strip trailing slashes and any subpath mount prefix before matching."""
    p = parsed.path
    # Strip trailing slash (but not the leading one)
    if len(p) > len("/api/kanban/") and p.endswith("/"):
        p = p.rstrip("/") or "/"
    # Strip subpath mount prefix — find first occurrence of /api/kanban/
    idx = p.find("/api/kanban/")
    if idx > 0:
        p = p[idx:]
    return p

Apply at the top of handle_kanban_get, handle_kanban_post, handle_kanban_patch, handle_kanban_delete. ~5 LOC each.

Tier 2 — friendlier error text (M2, ~10 LOC)

static/panels.js:1233 loadKanban() catch block. When e.status === 404 && /not found/i.test(e.message), render:

"WebUI couldn't reach the Kanban backend at <failing-url>. This can happen behind a reverse proxy that rewrites paths. Check your proxy config or post the request URL from DevTools → Network in #report-bugs. If you don't use a proxy, try a hard refresh first."

Console-log the actual failing URL. Even if Ma can't read DevTools, anyone helping them can.

Tier 3 — server-side dispatcher fall-through diagnostic (M3, ~5 LOC)

Replace the bare "not found" at server.py:133 with f"unknown route: {parsed.path}" so the JS error message contains the actual failing URL out of the box. This converts a black-box bug to a self-diagnosing one.

This is the highest-leverage fix because it surfaces the actual problem URL in any future bug report of this shape — including the next /api/foo endpoint that gets a trailing slash for an unrelated reason.

Tier 4 — Mac app reload menu (separate hermes-swift-mac issue)

WKWebView users have no way to hard-refresh. The Mac app's hand-rolled menu (AppDelegate.swift:628-652, see hermes-webui/hermes-swift-mac#77) doesn't include Reload. File a paired issue to add View → Reload (Cmd+R) and View → Force Reload (Cmd+Shift+R) that calls WKWebView.reloadFromOrigin() after clearing WKWebsiteDataStore. This isn't required for fixing the bug but it's necessary for users to recover from the bug class as a whole.

Acceptance

  1. curl http://127.0.0.1:8787/api/kanban/config/ (trailing slash) → 200 OK, same payload as no-slash
  2. curl http://127.0.0.1:8787/hermes/api/kanban/config (subpath) → 200 OK
  3. The JS catch block renders an error message containing the failing URL
  4. tests/test_kanban_path_normalization.py covers trailing slash + subpath cases for all 4 verb dispatchers
  5. Existing kanban tests continue to pass

Severity

M1 / sprint-candidate — user has live kanban data they can't see. Mac app has no force-reload escape hatch (see hermes-webui/hermes-swift-mac#77 for the missing menu items). Tier 1 + Tier 3 together unblock all known repros and make the next bug-of-this-shape self-diagnosing.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingrendererIssues related to the markdown/content renderersprint-candidateStrong candidate for next sprint

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions