Troubleshooting
- A Run Came Back Amber (
INCONCLUSIVE) - A Green Run Says
LLM-judgedorUnverified - This Red Is Already Known
- Run Hangs in Execute
401 authentication_error- Login Fails /
401on the UI (Not Claude OAuth) - "Env Is Busy" /
409When Starting a Run - I Can’t See My Run / Someone Else’s Run Is Hidden
- A Section Is Missing From the Sidebar
- Live Browser Blank / noVNC Won’t Connect
- Run Fails With
[tree-coverage] CRITICAL - Switched Settings to a New Env, Tests Fail / Device Not Found
- Imported Test Has Empty
emulator_setup - Step Fails With "Expected value not found"
- Backend Score Doesn’t Match TestRail Expected
- Tree Pushed But Portal Shows Old State
- Sidebar / Tab Click Times Out
- An Import Won’t Start
- Force Stop Didn’t Clean Up
- TestRail Modal Shows "Disconnected"
- Reading the Batch Report Failure Categories
Common failure modes and how to diagnose them. For an organised debugging playbook (TestRail → backend → frontend → ArgoCD logs), see the Common Pitfalls section of the User Guide.
|
Where to look first
|
The absence of a screenshot is evidence too. No screenshot plus a whole
segment reporting No result from nav executor usually means the page was
blocked — a native OS dialog, for instance — so there was no viewport to
capture.
|
A Run Came Back Amber (INCONCLUSIVE)
Symptom: the run is neither green nor red. One or more steps are marked with a
description starting [not measured] ….
This is the evidence gate, live since 2026-09-03. An LLM verdict must quote,
verbatim, the text from the captured page that decides the claim, and the runner
checks that the quote really occurs in what it captured. A PASS that quotes
nothing — or that was reached over an empty capture — is not a product failure
and is not a pass either, so it is recorded as inconclusive. A FAIL is applied
whatever it quoted: a false red is cheap and visible, a false green is neither.
What to do:
-
Read the
[not measured]line — it names the reason (nothing was captured, the quote was not found in the capture, the capture was empty). -
Decide whether the claim is measurable from the screen at all. If the expectation names something the portal never renders, the case needs a programmatic
*_verifystep or ablockedmarker — not a green. -
If the step should have been measured, check that it carries an
expectedand lands on a page whose body text was captured.
A deterministic verdict is never downgraded: a description prefixed [verify],
[polling] or [empty-state] means a helper measured the claim itself, and the
judge having nothing to quote says nothing about that measurement.
Rollback, if the gate itself is ever suspected: set FT_VERIFY_EVIDENCE=0 on
the worker. It changes only the consequence — the judge is asked for evidence
either way.
A Green Run Says LLM-judged or Unverified
The verdict tag beside a run says what looked at the claim, not whether the
claim held. Unverified means nothing asserted anything (a bare navigation, an
auto-filled step, a verify helper that was unavailable). LLM-judged means a
model read the page and decided.
Neither is an error, but neither is proof. The Test cases list shows the same
information per case as a claims chip (3/7 measured), and the run history
has a Pass · not measured filter to sweep exactly these. Cases that keep
coming back LLM-judged are candidates for a typed *_verify step.
This Red Is Already Known
A failure carrying a known: <bucket> badge is in the known-issues registry —
somebody already investigated it and recorded what it is. Read the badge title
before spending time on it; GET /api/known-issues returns the whole registry
with the bucket, the note and the action for each case.
Run Hangs in Execute
Symptom: the phase stepper sits on Execute for several minutes; MCP Log
shows the same browser_snapshot repeating without action.
-
Open the live view via the Live browser button (gateway-proxied noVNC for the run you own).
-
Identify what the browser is showing — is the dialog the agent expects there?
-
If the model in use is Sonnet, force-stop and rerun with Opus. Sonnet occasionally stalls on multi-step interact loops; Opus is the default for this reason.
-
If Opus also stalls, the test prompt is ambiguous — open Editor, refine the failing step’s
action/expected, save, rerun.
401 authentication_error
A Claude CLI auth failure raised mid-run by the agent’s LLM calls.
Since 2026-07-13 the workers authenticate with a long-lived
CLAUDE_CODE_OAUTH_TOKEN set in the Portainer stack environment (from
claude setup-token, valid about a year). It overrides any stale
.credentials.json on the bind mount, so there is no periodic refresh to run
and no laptop-side scheduled task — the old one was removed. A 401 here means
that token was revoked, rotated, or never set on this stack.
-
Check
GET /api/admin/credentials-status(admin session). -
Fix the stack env and redeploy.
POST /api/admin/refresh-credentialsstill exists for a manual push of a local credentials file.
The runner scores a CLI auth failure as a run error, not as a test
verdict — a stand problem must never look like a product failure.
|
This is the Claude OAuth error, raised mid-run by the agent’s LLM calls.
A 401 returned by the UI login is a different problem — see
Login Fails / 401 on the UI (Not Claude OAuth) below.
|
Login Fails / 401 on the UI (Not Claude OAuth)
Symptom: the :8083 UI rejects your sign-in, or API calls come back 401
Unauthorized from the gateway (not from a running test).
-
401on login means a wrong username/password, a disabled account, or an expired bearer-token session — log in again. -
After ~5 failed attempts within a minute the gateway rate-limits you and returns
429 Too many attempts; wait a minute and retry. -
On a fresh deployment the only account is the bootstrap admin
admin/admin. If that was changed and lost, an admin must reset it from the Users tab (or re-bootstrap the gateway).
This is unrelated to the Claude 401 authentication_error above, which is about
the agent’s LLM OAuth token, not your UI session. See Accounts, Roles & Audit.
"Env Is Busy" / 409 When Starting a Run
Symptom: starting a run returns 409 with a message like
the emulator for env '<env>' is busy.
Cause: another run already holds that environment. Exclusivity is keyed on the
env’s physical emulator backend (emulator_url), so two profiles that point at
the same emulator block each other, while envs on distinct emulators run in
parallel.
Fix: wait for the in-flight run to finish, or pick another environment in the first dropdown of the Test cases run bar. See gateway.adoc#selecting-env.
Not to Be Confused With a Blocked Case
A case whose infrastructure precondition is missing on this stand no longer
returns 409. It is recorded as a finished run with the grey BLOCKED status
and a reason, so the gap is visible in the history and in the batch report
instead of vanishing. To run it anyway, use Run anyway (force) in the report
panel (or ?force=true on POST /api/run).
I Can’t See My Run / Someone Else’s Run Is Hidden
Run history and active runs are owner-scoped: each account sees only the runs it started. Admins are the exception — they see all runs. If your history looks empty or a colleague’s run is missing, you are simply logged in as a non-owning, non-admin account. See Accounts & Roles.
Two things are deliberately not owner-scoped, and they are the right way to find out whether a stand is free:
-
the Wave in progress card above Overview and Runs — every batch in flight on any worker, whoever started it (env, worker, owner, model, progress, ETA);
-
the Busy column in the admin Workers section — what each worker is running right now.
GET /api/runs/active is owner-scoped and will happily report
"nothing running" while a colleague’s wave occupies the stand. Never use it to
decide whether it is safe to redeploy — read the wave card, the Busy column, or
the worker’s own access log.
|
A Section Is Missing From the Sidebar
Two independent reasons, and only the first is about roles:
-
Admin-only. Settings, Users, Workers and Activity are hidden for editor and viewer roles. Ask an admin to raise your role or to perform the action.
-
Hidden while empty. Learning, Procedures and Drafts were taken out of the sidebar on 2026-09-05 because they were carrying no content. This applies to everyone, admins included — it is not a permission problem. Their panels and their routes are untouched, so a direct link still opens them:
/#learning,/#knowledge,/#drafts.
The always-visible sections are Overview, Test cases, Runs and Editor.
Live Browser Blank / noVNC Won’t Connect
The live view is now gateway-proxied and owner-gated — the old direct noVNC host port was removed. You can only watch the browser of a run you currently own on that stand; the gateway closes the noVNC socket otherwise. Make sure a run of yours is actually active, that you are logged in as its owner, and then open the Live browser button.
Run Fails With [tree-coverage] CRITICAL
Symptom: the run is marked error immediately after start; the Output
pane shows a line like:
[tree-coverage] CRITICAL: tree=181.xml kpi=wlan-health-de-mesh | missing required prefixes: Device.WiFi.DataElements
Cause: the YAML’s kpi_type requires TR-181 paths that the configured
tree_file does not contain. The pre-flight (see
architecture.adoc#preflight) refuses to start a run that is guaranteed
to fail later in verify because the data simply isn’t there.
Fixes, in order of preference:
-
Switch
tree_fileto a template that covers the required data model (e.g.181-mesh-de.xmlforwlan-health-de-mesh). -
If no template fits, click Generate Tree in the Editor with the test text in place and let
bbf-tree-mcpproduce a fresh tree. -
As a last resort, hand-edit the XML under
/app/templates/setups/to add the missing object instances. Keep the tree minimal — extra paths can surface accidental defaults that change scoring.
A second, non-fatal validator writes [tree-coverage:intent] lines to the same
pane. Those describe what the case’s intent needs and never stop a run.
If the same line is [tree-coverage] WARN instead of CRITICAL, the run
proceeds but emulator_setup.parameters reference paths that aren’t in the
tree — the emulator will accept them but the parameter will not be visible to
the backend until a corresponding object instance is created.
The validator is schema-aware — it normalises any concrete instance
index (AccessPoint.2.AssociatedDevice.3.*) to .{i}. and matches against
the schema-level path set extracted from the tree, so dynamic instances
created at runtime by the emulator no longer trigger false-positive WARNs.
For neighboring_diag tests the validator also auto-swaps tree_file to a
template that already declares NeighboringWiFiDiagnostic.Result.{i} if the
configured tree lacks those instances.
|
Switched Settings to a New Env, Tests Fail / Device Not Found
Symptom: after changing the URLs in Settings to point at a different
environment (e.g. from test-agent to dev-mysql), runs go to the new
portal but the device the test expects is missing — the runner can’t find
it in the device list, or the portal shows no parameters.
Root cause: portal navigation uses base_url_* and direct emulator REST
queries use emulator_url — both honour Settings immediately. But the
device-creation path goes through the mcp-tr-emul sidecar, which
historically had its target ACS hard-coded.
Fix: set both Emulator MCP URL and ACS URL in Settings so they match the new environment:
-
Emulator MCP URL — usually unchanged (
http://mcp-tr-emul:8082) unless you’re running a separate sidecar per environment. -
ACS URL — must match the env (e.g.
http://ftacs.dev-mysql.rd.friendly-tech.com/ftacs/ACS). The runner writes this intoDevice.ManagementServer.URLright after starting the emulator, so the next Inform reaches the correct ACS.
After saving Settings, no restart is needed — the next run picks up the new URLs.
Imported Test Has Empty emulator_setup
The TestRail case has no XML tree attached and the converter fell back to a template that had no parameter requirements.
-
Open Editor, click Generate Tree with the test text in place.
-
Or set
tree_file: 181.xml(or another template) and addparametersmanually for the values the test expects to read.
Step Fails With "Expected value not found"
Programmatic verifier scanned the DOM and didn’t see the expected text. Check:
-
Did the runner navigate to the right page? Use MCP Log — the last
browser_navigateURL should match the widget being verified. -
Is the widget rendering the value you expect? Open VNC after a failed run to confirm.
-
Does the backend actually have the data? Hit the emulator API:
curl 'http://tr069-emulator.test-agent.rd.friendly-tech.com/apiV2/parametersByPrefix?serial=test-runner-001&prefix=Device.WiFi.AccessPoint.1.AssociatedDevice.1.' -
Or query the device tree from the portal via
FtApiClient. The portal tree reflects what the backend received over CWMP — if a parameter is in the emulator API but missing in the portal tree, the ACS didn’t pick it up.
Backend Score Doesn’t Match TestRail Expected
The widget value computed by the live backend differs from what the TestRail test expects. Order of investigation:
-
Spec docs —
C:\F_Projects\ft-device-network\docs\src\modules\ROOT\pages\(mesh/client-health-score.adoc,network-score.adoc, etc.). Source of truth for KPI formulas, data-model paths, weights, fallback paths. -
Backend Java sources —
NetworkScoreProcessorand*ScoreCalculator`s in `C:\F_Projects\ft-device-network. Implementation of the formulas with exact border values. -
Frontend —
C:\F_Projects\oneiot_angular. How the value is rendered (rounding, transformation). -
ArgoCD logs —
ft-device-networkpod. Look forNetworkScoreProcessor: TH: …, INT: …, NH: …lines for WLAN; tail to see what the backend computed and whether the ACS delivered the new state.
| The expected value in TestRail is the source of truth — never edit it. Fix the converter or emulator parameters. |
Tree Pushed But Portal Shows Old State
The portal shows whatever the ACS has cached from the last CWMP Inform. If the emulator was reset but no Inform was triggered, the portal lags.
-
Check the MCP Log for
invoke_eventafter the tree push. -
Manually trigger via the emulator API
/apiV2/invokeEvent?serial=…&type=2 PERIODIC. -
Or restart the emulator instance via
start_protocol_emulator.
Sidebar / Tab Click Times Out
Selector text doesn’t match what the portal shows. Two common reasons:
-
Wrong portal — the YAML says
portal: supportbut the case is for management. Check the top-level field. -
Localisation / casing —
nav_targetis matched case-sensitively. Open VNC, copy the exact label, paste intonav_target.
An Import Won’t Start
POST /api/testrail/import answers 409 import already running (job …). Only
one import may be in flight per worker. Poll
GET /api/testrail/import/jobs/{job_id} for the running one and wait for it —
the modal does this for you and shows Created / Skipped / Errors when it lands.
Force Stop Didn’t Clean Up
In rare cases the Claude CLI subprocess survives Force Stop. The next run
will queue but never start because Playwright is still bound to the previous
session.
-
docker exec test-runner pkill -f claude(via Portainer terminal). -
Or restart the stack from Portainer.
A force-stopped case is recorded with status error and the reason
"Stopped by user". There is no run status stopped — stopped exists only as a
batch status and as a failure category.
|
| Never redeploy a stack to clear a stuck run without first checking that no batch is in flight on it. Recreating the container cuts the running case in half: its pre-flight has already created domains, users and moved devices, and the post-flight that would clean them up never executes. |
TestRail Modal Shows "Disconnected"
-
Open Settings → TestRail.
-
Verify URL, Email, API Key.
-
Save. The pill should turn green within a couple of seconds.
-
If still red, check the agent logs for HTTP errors against TestRail — often a stale API key after rotation.
Reading the Batch Report Failure Categories
Each failure in a batch report is bucketed into a category by the post-run analyser. This drives the doughnut chart in the Batch Report dialog and the per-row label in the results table.
A run whose status is inconclusive is given no category at all,
so it never appears in the breakdown or in the failure counts. Read the
Inconclusive tile, not the doughnut — a batch can look almost clean there and
still have measured almost nothing. See A Run Came Back Amber (INCONCLUSIVE).
|
| Category | Meaning |
|---|---|
|
|
|
The Playwright MCP didn’t respond within the per-call timeout. Usually the browser hung on a snapshot or a long network call. Retry or restart the runner if it persists. |
|
A |
|
Steps executed but the verifier (DOM scan or LLM fallback) didn’t see the expected value. Usually a real backend / converter mismatch — start with the debugging order in user-guide.adoc#common-pitfalls. |
|
The Smart Converter raised an error on this YAML before the run could
start (missing required field, malformed |
|
The user pressed Force stop (or the Force Stop API was called). Not a real failure. |
|
Couldn’t classify. Check the run’s Output pane for the raw error. |