Troubleshooting¶
Direct interception¶
The sender reaches the receiver but AIT shows no messages¶
Confirm that the sender uses the loopback listener printed by
ait intercept start, not the original upstream. AIT uses explicit placement;
it cannot observe traffic that bypasses its relay or wrapper.
Messages appear but do not pause¶
Turn Intercept on. If break conditions exist, temporarily remove them or check protocol, direction, operation, route, source/destination, task, context, and artifact values against the message row.
Requests and responses both pause¶
An enabled session with no break conditions pauses every decoded message. Add
a request direction condition when you only want outbound requests.
The edit will not forward¶
Structured mode requires valid JSON and a protocol shape the transport encoder can preserve. The editor shows the parse/encode error and retains the original. Correct the value or choose Forward original. Use Raw only when exact bounded frame editing is necessary.
The pending queue is full¶
AIT fails visibly rather than bypassing interception. Decide existing pending
messages or stop with an explicit --pending forward|drop policy, then retry.
Stop asks about pending traffic¶
Paused messages do not expire. Choose whether shutdown forwards each unchanged or drops it. AIT will not decide silently.
The "Original" pane does not match what the sender sent¶
That is what an armed transform rule looks like. A rule mutates the message
before it reaches the gate, so the pane shows sender_original and the
rule-applied candidate separately. If only one is shown, the session has no
armed rule and the two are the same message.
No rule matches¶
Check the transcript before checking the target. A rewrite should show a
rule_applied value.
ait traffic tail --run RUN_ID --limit 50 --json
ait traffic inspect --run RUN_ID --cursor RECORD_CURSOR --related-limit 20 --json
Every non-matching rule reports a stable miss code rather than silence:
| Code | Meaning |
|---|---|
predicate_mismatch |
The rule was eligible; a where predicate did not hold. |
missing_path |
The mutation path does not exist in this message. |
decoder_ineligible |
The transport or decoder cannot carry this rule's paths. |
shadowed |
An earlier rule matched first: the engine returns on first match. |
disabled |
The rule is loaded but switched off. |
chain_guard |
A chain.* guard held the rule back. |
chain_state_unavailable |
The chain ledger could not be read for this decision. |
chain_reference_unresolved |
The rule references a rule ID that is not loaded. |
shadowed is the one that surprises people. Set priority: on the rule that
should win, or reorder the set through
PUT /api/v1/intercepts/{session_id}/rules/order. Note what priority does and
does not do. It makes shadowing declared, not absent. Two rules matching the
same message still produce one winner and one shadowed: priority only decides
which. See the Rule-Miss Playbook.
To tune a rule offline against traffic you already captured, replay the rule set over the transcript instead of re-running the attack:
MCP stdio does not start or stop¶
- confirm the packaged Seam binary, child executable and
cwdexist; - use an argument array rather than a shell string;
- confirm every
env_refssource variable exists; - raise bounds only after inspecting bounded stderr;
- on cancellation, inspect the job and transcript summary rather than relaunching immediately.
MCP or A2A correlation is lost¶
Separate handshake and session failures from rule misses. Verify JSON-RPC request IDs, MCP protocol/session headers, and A2A context/task/artifact identifiers. A rule miss reports a miss code; a transport failure does not, and surfaces as the transport's own error on the record. If neither appears, the message was never decoded -- check transport and decoder eligibility first.
A JSON-RPC response carries no method, so its kind is resolved from the correlated request. If responses show a generic kind, the request they answer was not captured: usually because interception started mid-session.
A shadow endpoint received nothing¶
- confirm the substituted value in the delivered message really points at the shadow's URL, not the original upstream: open the message comparison;
- confirm the shadow is still running (
ait intercept shadow list); - confirm the receiver actually re-resolves the endpoint. An agent that cached an Agent Card at startup will not follow a substitution made afterwards, and that is a finding about the receiver, not a fault in the shadow.
Shadows bind to loopback only. There is no remote-bind option, so a target on another host cannot reach one.
Lab startup fails¶
Check for port conflicts first.
The nine built-in exercises run as separate local processes over loopback or stdio. They need no Docker daemon, model provider, framework package, or external network. If one fails to start, the previous lab's components are usually still holding their ports. Stop it explicitly rather than killing the process, so the cleanup receipt is written.
State adapter or restore fails¶
- confirm
state_jsonlhas a command or supportedqdrant/chromaadapter; - confirm collection/namespace scope is disposable;
- run
ait state snapshotbefore the attack arm, not after; - distinguish adapter failure from contamination, where restore completed but the digest differed;
- use
ait state restore ... --yesonly after reviewing target and scope.
Provider or media delivery fails¶
- inspect media before materialization and use PNG, JPEG, WAV, PDF or UTF-8 text;
- pass deposited manifests in
media_manifest_refs; - keep
ait-media://Nindexes within the deposited list; - distinguish malformed JSON from SSE framing failures;
- check request, response and token bounds;
- remember retries default to zero.
Missing report¶
ait report looks for lab/report/report.md or reports/report.md.
If no report exists, inspect logs/lab.log and confirm the Assay report render
step completed.
Schema or hash verification fails¶
Use the shared schema path from the repo root:
agentic-redteam/seam/seam transcript verify \
--schema agentic-redteam/schema/transcript.schema.json \
--transcript out.json
Failures usually mean the file was truncated, edited by hand, written by an older schema version, or captured with a missing schema path. Transcript verifiers support chain 1.0, 2.0 and 2.1.
Oracle did not observe the side effect¶
Assay will not accept agent text as proof. Check:
- the oracle file or callback endpoint was reset before each arm;
- the target actually writes the tripwire on success;
- both arms are reaching the same target surface;
- the expected string or JSON field matches what the target writes.
If the receiver said it did the thing but no oracle observed it, the finding
stops at receiver_processed. Agent narration is never evidence.
HTTPS or TLS confusion¶
Seam is not a transparent TLS interception appliance. Route plaintext test traffic through Seam explicitly, or configure a target/client test mode where Seam receives HTTP, SSE, WebSocket, or stdio application traffic directly.
Remote bind or API access fails¶
Hardened defaults prefer loopback. Remote listeners, remote intercepts, redirects, and API tokens must be configured explicitly for authorized ranges. Profiles are convenience presets, not hidden permission systems; direct flags remain authoritative.
A2A binding and lifecycle problems¶
- Binding rejected: inspect requested/offered/rejected fields in the Agent Card and the transaction. AIT never silently downgrades.
- Illegal cancel/complete state: compare the expected order with the actual acknowledgements. Timing-only sleeps are not confirmatory.
- Stream becomes idle: inspect the 30-second default idle bound separately from the five-minute total bound. AIT does not reconnect automatically.
- Events arrive out of order: duplicate and out-of-order events have separate diagnostics; do not collapse them.
- Callback authentication fails: verify the credential reference and audience at request time. Do not place a callback secret in target metadata.
- gRPC bridge exits: verify the
a2a-grpcextra, pinned protobuf compatibility, endpoint/authority, TLS references, and message bounds. This bridge supports only official A2A service methods, and AIT does not fall back to HTTP.
Credentials appear in a captured file¶
They are supposed to. Credential masking was removed so token forging, stripping, downgrade and relay can actually be tested: a substituted destination receiving a bearer token is the credential-relay finding, and masking it would have destroyed the evidence.
What this means for handling: transcripts, session files, shadow observations
and rule exports are written 0600. Export with --redacted (the default,
a structural allowlist) before handing a record to anyone. --raw is complete
capture and is labeled as such. It is never labeled "sanitized".