Troubleshooting: Symptom to Action
Troubleshoot in layers. Do not jump to protocol decoding when the installed binary, license, NIC, port or gRPC service has not been proven. Every JSON failure carries a stable error code (CL-USAGE-001, CL-CONFIG-001, CL-LICENSE-001, CL-PROFILE-001, CL-RUNTIME-001, CL-ARTIFACT-001, CL-CONTRACT-001, CL-UNSUPPORTED-001, CL-IO-001, CL-SERIALIZATION-001, CL-INTERNAL-001) and an operator_action; read them first (exit codes).
Fast triage order
- Identity — version, license, profile.
- Environment — doctor, roots, permissions, NIC.
- Runtime — stale sessions, role locks, port ownership, gRPC health.
- Transport — IPv6 zone, SDP, TCP, TLS.
- Protocol — V2GTP, EXI, message sequence, response budgets.
- Integration — VeriStand mapping, automation client.
- Evidence — run ID, trace, first error timestamp.
Failure matrix
| Symptom | Usually means | Check first | Corrective action |
|---|---|---|---|
chargelink not found | PATH or installation path | Test-Path $CL | Use the full path; reopen the shell after changing PATH. |
Exit 2, CL-USAGE-001 | Wrong subcommand or flag for the installed version | chargelink <cmd> --help | Compare with the CLI reference; scripts from other releases must be checked. |
Exit 70, CL-UNSUPPORTED-001 | The command is accepted by the parser but has no implementation in the installed version | — | Use the commands documented in this manual (for example test instead of scenario/suite/campaign). |
Exit 20, CL-LICENSE-001 | No active or supplied license, or a package entitlement is missing | license status, license entitlements | Activate the supplied file; confirm the purchased package entitlement. |
Exit 10, CL-CONFIG-001 | Invalid configuration (for example --backend ethernet, empty PFX password variable, non-positive live-case value) | the message field | Correct the option; the message names the accepted values. |
Exit 40, CL-RUNTIME-001, "role lock" | Another EVSE/EVCC session is live or stale in this runtime root | runtime list, runtime locks | evse stop / evcc stop, or runtime recover --stale-only. |
| Different roots see different sessions | Missing --runtime-root/--artifact-root; defaults are relative to the working directory | the runtime_root field in any result | Pass the same two root options on every command. |
| EVSE cannot bind / address in use | Another process owns UDP 15118 or the TCP port | netstat -ano | findstr ':15118 :50118' | Stop the owner or use --tcp-port 0. |
| EVCC never discovers the EVSE | Wrong NIC, IPv6 disabled, zone index, firewall | Get-NetAdapter, IPv6 binding, --interface-index, rules for UDP 15118 from fe80::/10 | Correct the adapter and advertised address; recreate the firewall rules. |
| "unreachable network" (10051) on the EVCC | Peer address carries the peer's zone index instead of this machine's | --peer-ipv6 suffix | Use fe80::<peer>%<LOCAL_INDEX>. |
| SDP succeeds, TCP fails | Advertised address or port not reachable | sdp_diagnostics.json, evse_listener_status.json | Advertise the bench adapter's link-local address; open the TCP port from fe80::/10. |
| TCP succeeds, TLS fails | Version, chain, clock or missing client certificate | tls_handshake_diagnostics.json, evse tls-pki-doctor, w32tm /query /status | See TLS, PKI and Plug & Charge. ISO 15118-20 needs TLS 1.3 mutual; do not use the SChannel or dev adapters. |
| TLS succeeds, then no protocol progress | Request encoding not matched by the reference responder, or response budget exceeded | traces\live-v2gtp-frames.jsonl, session_handshake_artifact_gate.json | Identify the first unmatched or rejected frame; escalate with the trace. For ISO 15118-20 dynamic behaviour use the plant-coupled lane. |
| gRPC health fails | Server not running, wrong endpoint, port conflict | Test-NetConnection 127.0.0.1 -Port 50051 | Start integration serve; check the state root. |
| gRPC healthy, automation fails | Client request or API version mismatch | integration session-smoke, capabilities.api_version | Prove the built-in smokes, then debug the client against the proto of the same version. |
GetProtocolState is unobserved | By design: the session carries no "listener" object, or the listener was started from the CLI | bound-live-protocol-observer capability | Bind the session or read the listener artifacts; this is not a fault. |
Every gRPC or REST call fails with CL-RUNTIME-001 "does not have valid authentication credentials" | The client has no token or an old one (the server writes a new api-token at every start) | the message names the file it tried | Set CHARGELINK_API_TOKEN_FILE to the running server's <state-root>\api-token, or start the server with the runtime root's default state root. |
integration serve exits 40 at start | --listen names a non-loopback address | the diagnostic envelope | Use 127.0.0.1 or [::1]; remote access is not offered. |
| VeriStand bench: owner host stops before the listener starts | NI version, another deployed project or VeriStand client, leftover processes | <run>\host.log, the failure file in host-output, NI file hashes | Undeploy and close the other VeriStand client; end leftover chargelink.exe and ChargeLink.Veristand.XilHost processes; see NI VeriStand Bench, Troubleshooting. |
| VeriStand bench: plant values do not move | Changed system definition or contract files, safe start-up state held, safety input active | manifest\SHA256SUMS, Send-OwnerCommand.ps1 -Command GetStatus | Restore the package files from the verified ZIP; see VeriStand Signal Mapping. |
| Rate stabilisation timeout | Engine rate too high for the PC | engine rate, power plan, CPU load | Use the 500 Hz SIL configuration; repeat the timing check. |
Scripted DC-CAN "hardware" run has hardware_loop_evidence_complete: false | Expected: the scripted lanes start no CAN driver; hardware mode records an operator attestation | — | None; see Scripted DC-CAN Lanes. |
Test case shows inconclusive or blocked | No evidence bound, or prerequisite absent | campaign_status.json | Attach an evidence manifest (test run --evidence live); satisfy or document the blocker. Never relabel as FAIL. |
| No artifacts | Wrong artifact root or permission | global --artifact-root, directory ACL | Use an explicit writable root; preserve any failure evidence before retrying. |
| Integrity check fails | Files changed after the run | artifacts integrity output | Quarantine and repeat the run; do not regenerate the manifest. |
Layer checks
Identity and environment
chargelink --version
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json license status
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json doctor --strict
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json profile show <PROFILE_ID>
Runtime
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json runtime list
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json runtime locks
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json runtime inspect --run-id <RUN_ID>
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json runtime recover --stale-only --dry-run
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json runtime recover --stale-only
Transport
Get-NetAdapter | Format-Table ifIndex,Name,Status -AutoSize
Get-NetIPAddress -AddressFamily IPv6 | Where-Object IPAddress -like 'fe80::*' | Format-Table InterfaceIndex,InterfaceAlias,IPAddress -AutoSize
ping -6 'fe80::<PEER>%<LOCAL_INDEX>'
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse live-preflight --source external-evcc --protocol <P> --profile-id <ID> --host '::' --tcp-port 50118 --udp-port 15118 --advertise-ipv6 $Advertise --interface-index $IfIndex
Protocol
Get-Content "$Data\artifacts\<RUN_ID>\traces\live-v2gtp-frames.jsonl" | Select-Object -First 40
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse live-closure-gate --run-id <RUN_ID> --source external-evcc --require-sdp --require-tcp-peer --require-v2gtp --require-exi
Useful separation: SDP → TCP → TLS → V2GTP → EXI/application → charging sequence. A later-layer failure does not invalidate earlier transport success; say which layer failed when escalating.
Prepare a support bundle
Preserve the failing run directory, then follow Support Escalation. Do not rerun over the same run ID.

