# 07 — Troubleshooting

The first questions to ask when something isn't working, and where to look
for answers.

## Where to look

| Symptom is on the… | Look here |
|---|---|
| Cloud (UI, missing data) | **Lab → Logs** (`/lab/logs`) → category `result`/`sync`/`system` |
| Cloud (deeper / raw payloads) | `lab_communication_logs` table → `raw_message` column |
| Agent (clinic PC) | Windows Event Viewer → Application → source `LabBridgeAgent`. Or `labbridge.exe run` (foreground) for live output |
| Agent (queued items) | SQLite at `data_dir/queue.sqlite` — query with `sqlite3` or DB Browser for SQLite |
| Analyzer | Analyzer's own service menu / printed self-test |

## Cloud: install / module problems

### "Class … does not exist" after installing

Clear caches:

```bash
php artisan config:clear
php artisan route:clear
php artisan view:clear
composer dump-autoload -o
```

### Migrations show as Pending even after install

The Modules admin screen's **Install** button must be clicked while signed
in as a superadmin. Or run from CLI:

```bash
php artisan module:migrate LabCore
php artisan tinker --execute='\App\System::addProperty("LabCore_version","1.0.0");'
```

### "No drivers" on the dashboard

No vendor module is installed yet. Install AbaxisLab (or another vendor
module) per [01 Installation § Install a vendor module](01-installation.md#install-a-vendor-module).
Then refresh.

## Agent isn't reaching the cloud

The agent shows up in **Lab → Agents** but **Connectivity** stays
**Offline**.

### Step 1 — verify the agent is running

Windows:
```bat
sc query LabBridgeAgent
```
Should show `STATE: RUNNING`. If not: `labbridge.exe start`, and check the
Event Viewer for the failure reason.

### Step 2 — verify network reachability

From the clinic PC:

```bat
curl -i -H "Authorization: Bearer <token>" ^
     https://erp.example.com/api/lab/agent/heartbeat ^
     -X POST -H "Content-Type: application/json" ^
     -d "{\"queue_depth\":0}"
```

Expected: `HTTP/1.1 200`. Common failures:

| Curl says | Means |
|---|---|
| `Could not resolve host` | DNS / `cloud_url` typo |
| `Failed to connect / timed out` | Firewall blocks outbound 443; or wrong port |
| `SSL certificate problem` | The clinic PC doesn't trust the cert chain — install the CA bundle |
| `401 invalid_agent_token` | Token in `config.yaml` doesn't match the cloud row — regenerate + paste again |
| `404` | URL prefix mismatch — note the path is `/api/lab/agent/heartbeat`, not `/lab/api/...` |

### Step 3 — verify the agent picked up the new config

After editing `config.yaml`, you must restart:

```bat
labbridge.exe restart
```

Then watch the Event Viewer — the first log line on startup confirms the
config it loaded.

## Analyzer connects, but no results stored

The analyzer's link light blinks, the agent log shows a connection, but
nothing appears in **Lab → Results**.

### Check the communication log

```sql
SELECT created_at, level, category, message, raw_message
FROM lab_communication_logs
WHERE category IN ('result','connection','sync')
ORDER BY id DESC LIMIT 30;
```

What you'll typically see:

| Log message | Means | Fix |
|---|---|---|
| `Raw result message received from agent` then `Result ingestion failed: …` | Parser crashed on this vendor's dialect | Capture the `raw_message`, inspect, adjust the driver's `parse()` / `normalize()` |
| `status: no_driver` | Wrong `vendor` in the agent's payload (or the vendor module isn't installed) | Verify `lab_devices.vendor` matches a registered driver |
| `status: not_a_result` | Message is a worklist query, not a result push | This is normal for bidirectional analyzers — only `R`-bearing messages become sessions |
| (nothing at all) | Agent has the message queued but the sync sweep isn't running, or the cloud is unreachable from the agent | Check agent heartbeat; check `data_dir/queue.sqlite` for backed-up `status='pending'` rows |

### Inspect the agent's queue

```bash
sqlite3 /path/to/data/queue.sqlite
sqlite> SELECT status, attempt_count, substr(last_error, 1, 80), created_at
        FROM queue ORDER BY id DESC LIMIT 20;
```

`status='pending'` rows that aren't decrementing → the agent can't reach
the cloud; see *Agent isn't reaching the cloud* above.

`status='failed'` rows with `last_error` populated → look at the error
text; usually a 422 from the cloud with the parser's complaint.

## Results aren't showing in the UI

Sessions are stored (`SELECT count(*) FROM lab_test_sessions` is non-zero)
but **Lab → Results** is empty.

The screen filters on the **current business**. Inspect:

```sql
SELECT id, uuid, business_id, vendor, sample_no
FROM lab_test_sessions ORDER BY id DESC LIMIT 10;
```

If `business_id` is NULL, the session was ingested from an agent with no
`business_id` set. Set the agent's business:

```sql
UPDATE lab_agents SET business_id = <your-business-id> WHERE code = '<agent-code>';
```

And future sessions will inherit it. (Also fix the orphans: `UPDATE
lab_test_sessions SET business_id=<id> WHERE business_id IS NULL AND
agent_id IN (SELECT id FROM lab_agents WHERE business_id=<id>);`)

This is exactly the bug the Abaxis simulator hit during browser-testing in
Phase 4 — fixed in the simulator's `simulatorAgentToken()`. The same
principle applies to any agent: a row created via the **Add Bridge Agent**
UI button inherits the current session's business automatically.

## Duplicate sessions

You should never see duplicates — every push is idempotent on the agent's
UUID. If you do see them:

| Cause | Check |
|---|---|
| Two different agents generated different UUIDs for the same physical run (e.g. analyzer wired to both agents simultaneously) | Decommission one |
| Manual `INSERT` into `lab_test_sessions` bypassing the unique constraint | Honor the `lab_session_vendor_sample_date_unique` index |
| Different `test_date` on the rows | Verify the analyzer's clock is correct; the unique key is `(vendor, sample_no, test_date)` |

## Heartbeat OK, results missing for one specific device

The agent is fine; the analyzer's listener might not have opened:

1. **Lab → Devices** — is the device's `status` set to `disabled`?
2. Agent logs — is there a "listener: device N TCP on :PORT" line on
   startup? If the line is missing, the device row was added **after** the
   agent started — restart the agent.
3. Two devices with the same port → only one listener opens; the agent
   logs the second one as "address already in use".

## Service won't start (Windows)

`labbridge.exe start` returns immediately with `start: Failed to start the
service` (or Event Viewer shows it died on launch).

| Cause | Fix |
|---|---|
| Bad `config.yaml` (missing keys, invalid YAML) | `labbridge.exe run` — runs in foreground and prints the load error |
| `data_dir` not writable by `LocalSystem` (or the service account) | Move it to `C:\ProgramData\LabBridge\data` or change the service account |
| Port already in use | Another process is bound to a TCP port the agent wants — `netstat -ano | findstr :9600` |
| Missing Visual C++ runtime | The agent is pure-Go so this shouldn't matter; if it does, you somehow built with cgo — rebuild with `CGO_ENABLED=0` |

## Permission denied in the UI

`/lab/...` returns 403. The user lacks the relevant `lab.*` permission.
Either:

- Give the user the business Admin role (which has every `lab.*` perm), or
- Edit the role in Spatie Permissions and add specific perms
  (`lab.access_module`, `lab.view_results`, …).

## Replaying a stuck queue manually

If the agent's queue is wedged and you want to force a flush:

```bash
labbridge.exe restart
```

The agent runs a sweep immediately on startup. If it still doesn't drain,
inspect the queue (see above) and the `last_error` column. As a nuclear
option you can re-queue everything:

```bash
sqlite3 /path/to/data/queue.sqlite
sqlite> UPDATE queue SET status='pending' WHERE status='failed';
sqlite> .quit
```

…and restart the agent. The cloud will idempotently deduplicate.

## When you've tried everything

Capture and share:

1. **Cloud:** `SELECT * FROM lab_communication_logs ORDER BY id DESC LIMIT 30;`
   (or screenshot of **Lab → Logs**).
2. **Agent:** Event Viewer export of the LabBridgeAgent source for the
   last hour, plus the output of `labbridge.exe run` in another terminal
   reproducing the issue.
3. **Queue:** `data/queue.sqlite` (small file, fine to share).
4. **Versions:** `LabCore_version`, `AbaxisLab_version`, the agent binary
   build date.
