---
title: "explain which expectation a recorded request matches now, and why higher-priority ones lost"
method: PUT
path: "/mockserver/explain"
tags: ["troubleshooting"]
---

# explain which expectation a recorded request matches now, and why higher-priority ones lost

`PUT /mockserver/explain`

## Request

- Base URL: `http://localhost:1080`
- URL: `http://localhost:1080/mockserver/explain`
- Auth: none declared

Re-checks one recorded request, identified by the correlationId of its log entries, against the current expectations in the order MockServer evaluates them. Returns the expectation that would serve the request now, why each expectation evaluated ahead of it was passed over (reason NAMESPACE, INACTIVE, FIELDS_DIFFER, NOT_OPERATOR, CONVERSATION, SCENARIO_STATE or PERCENTAGE, with per-field differences and remediation hints), which lower-priority expectations it shadows, and how the request was handled when it arrived. The result is recomputed on demand, so it reports whether the expectations changed since the request arrived. Values quoting the request are redacted as the event log is. At most 500 expectations are evaluated. With ?narrate=true the response also carries an AI-generated plain-language summary from the configured LLM backend in 'narration'.

## Query parameters

- `correlationId` string
- `narrate` boolean

## Request body

- object
  - `correlationId` string: correlationId of the request to explain, when not given as a query parameter

## Response `200`

explanation returned

- RequestExplanation: which expectation would serve a recorded request now, why each expectation evaluated ahead of it was passed over, and how the request was handled when it arrived (PUT /mockserver/explain)
  - `correlationId` string: the correlationId of the explained request's log entries
  - `timestamp` string, date-time: when this explanation was computed
  - `recomputed` boolean: always true; the explanation is recomputed on demand, not recorded when the request arrived
  - `note` string: a reminder that the result reflects the current expectations, scenario states and remaining Times
  - `request` object: the recorded request that was explained
    - `timestamp` string: when the request was recorded, as the event log formats it (yyyy-MM-dd HH:mm:ss.SSS)
    - `method` string
    - `path` string
    - `bodyTruncated` boolean: present and true when the event log kept only the first maxLoggedBodyBytes of the body
    - `bodyTruncatedNote` string
  - `atArrival` object: how the request was handled when it arrived, read from the event log
    - `outcome` 'MATCHED' | 'NO_MATCH' | 'FORWARDED_WITHOUT_EXPECTATION' | 'RESPONDED_WITHOUT_EXPECTATION' | 'UNKNOWN'
    - `expectationId` string: the expectation that served the request when it arrived (outcome MATCHED only)
    - `expectationStillStored` boolean: whether that expectation is still stored (outcome MATCHED only)
    - `forwardFailure` object: present when the request was forwarded and the forward failed, so MockServer answered 502; the outcome is then MATCHED with the forward expectation's id, or FORWARDED_WITHOUT_EXPECTATION for a request proxied without one. The same facts as the MCP explain_forward_failure tool gives for this request
      - `statusCode` integer: the status MockServer answered with (502)
      - `reason` string: why the forward failed, redacted as the event log is and capped at 2048 characters
      - `reasonTruncated` boolean: present and true when the reason was capped
      - `causeNamed` boolean: whether MockServer named the cause; when false the reason is the raw error
      - `forwardLoopSuspected` boolean: whether the forward appears to have gone back to MockServer itself
      - `hint` string: what to look at next, present when causeNamed is false
  - `sameOutcomeAsAtArrival` boolean: whether the request would be handled the same way now; absent when the outcome at arrival is RESPONDED_WITHOUT_EXPECTATION or UNKNOWN
  - `expectationsChangedSince` boolean: true when the expectations changed at or after the moment the request arrived
  - `expectationsLastChanged` string, date-time: when the expectations last changed; absent when they never have
  - `matchedExpectation` ExplainedExpectation: one expectation as a diagnostic endpoint evaluated it. PUT /mockserver/explain fills every field but suggestedFix; the matched expectation has no reason, every skipped or shadowed one has a reason and an explanation. PUT /mockserver/debugMismatch fills expectationId, expectationMethod, expectationPath, matches, matchedFieldCount, totalFieldCount and differences. PUT /mockserver/explainUnmatched fills those plus differingFieldCount and remediation, and suggestedFix when asked for.
    - `expectationId` string
    - `expectationMethod` string
    - `expectationPath` string
    - `position` integer: the expectation's 1-based position in the order MockServer evaluates expectations
    - `priority` integer
    - `reason` 'NAMESPACE' | 'INACTIVE' | 'FIELDS_DIFFER' | 'NOT_OPERATOR' | 'CONVERSATION' | 'SCENARIO_STATE' | 'PERCENTAGE' | 'LOWER_PRIORITY'
    - `explanation` string: why the expectation was passed over, in plain language
    - `matches` boolean: whether the request matches the expectation's request matcher; absent when an earlier gate decided
    - `matchedFieldCount` integer
    - `totalFieldCount` integer
    - `differingFieldCount` integer
    - `differences` object: for each differing field, the differences found
    - `remediation` object: for each differing field, a hint on how to change the expectation
    - `suggestedFix` SuggestedFix: an RFC 6902 JSON patch of an expectation, as PUT /mockserver/retrieve?type=active_expectations returns it, that would make it match the request (PUT /mockserver/explainUnmatched with suggestFixes); nothing is applied
      - `patch` object[]: the patch operations, each under /httpRequest; empty when no field could be patched
        - `op` 'add' | 'remove' | 'replace' | 'test'
        - `path` string: a JSON pointer into the expectation
        - `value` unknown
      - `patchedFields` string[]: the differing fields the patch changes
      - `unpatchedFields` object[]: the differing fields the patch leaves alone, each with the reason
        - `field` string
        - `reason` string
      - `matchesAfterPatch` boolean: whether the patched expectation matches the request; false when the patch is empty
  - `closestExpectationId` string: when no expectation would serve the request now, the one with the fewest differing fields
  - `skippedExpectations` ExplainedExpectation[]: expectations evaluated ahead of the matched one, each with a reason and explanation
    - `expectationId` string
    - `expectationMethod` string
    - `expectationPath` string
    - `position` integer: the expectation's 1-based position in the order MockServer evaluates expectations
    - `priority` integer
    - `reason` 'NAMESPACE' | 'INACTIVE' | 'FIELDS_DIFFER' | 'NOT_OPERATOR' | 'CONVERSATION' | 'SCENARIO_STATE' | 'PERCENTAGE' | 'LOWER_PRIORITY'
    - `explanation` string: why the expectation was passed over, in plain language
    - `matches` boolean: whether the request matches the expectation's request matcher; absent when an earlier gate decided
    - `matchedFieldCount` integer
    - `totalFieldCount` integer
    - `differingFieldCount` integer
    - `differences` object: for each differing field, the differences found
    - `remediation` object: for each differing field, a hint on how to change the expectation
    - `suggestedFix` SuggestedFix: an RFC 6902 JSON patch of an expectation, as PUT /mockserver/retrieve?type=active_expectations returns it, that would make it match the request (PUT /mockserver/explainUnmatched with suggestFixes); nothing is applied
      - `patch` object[]: the patch operations, each under /httpRequest; empty when no field could be patched
        - `op` 'add' | 'remove' | 'replace' | 'test'
        - `path` string: a JSON pointer into the expectation
        - `value` unknown
      - `patchedFields` string[]: the differing fields the patch changes
      - `unpatchedFields` object[]: the differing fields the patch leaves alone, each with the reason
        - `field` string
        - `reason` string
      - `matchesAfterPatch` boolean: whether the patched expectation matches the request; false when the patch is empty
  - `shadowedExpectations` ExplainedExpectation[]: lower-priority expectations that also match but are never reached (reason LOWER_PRIORITY)
    - `expectationId` string
    - `expectationMethod` string
    - `expectationPath` string
    - `position` integer: the expectation's 1-based position in the order MockServer evaluates expectations
    - `priority` integer
    - `reason` 'NAMESPACE' | 'INACTIVE' | 'FIELDS_DIFFER' | 'NOT_OPERATOR' | 'CONVERSATION' | 'SCENARIO_STATE' | 'PERCENTAGE' | 'LOWER_PRIORITY'
    - `explanation` string: why the expectation was passed over, in plain language
    - `matches` boolean: whether the request matches the expectation's request matcher; absent when an earlier gate decided
    - `matchedFieldCount` integer
    - `totalFieldCount` integer
    - `differingFieldCount` integer
    - `differences` object: for each differing field, the differences found
    - `remediation` object: for each differing field, a hint on how to change the expectation
    - `suggestedFix` SuggestedFix: an RFC 6902 JSON patch of an expectation, as PUT /mockserver/retrieve?type=active_expectations returns it, that would make it match the request (PUT /mockserver/explainUnmatched with suggestFixes); nothing is applied
      - `patch` object[]: the patch operations, each under /httpRequest; empty when no field could be patched
        - `op` 'add' | 'remove' | 'replace' | 'test'
        - `path` string: a JSON pointer into the expectation
        - `value` unknown
      - `patchedFields` string[]: the differing fields the patch changes
      - `unpatchedFields` object[]: the differing fields the patch leaves alone, each with the reason
        - `field` string
        - `reason` string
      - `matchesAfterPatch` boolean: whether the patched expectation matches the request; false when the patch is empty
  - `totalExpectations` integer: how many expectations are stored
  - `evaluatedExpectations` integer: how many expectations were evaluated, at most maxExpectationsEvaluated
  - `truncated` boolean: true when the evaluation budget ran out before every expectation was evaluated
  - `maxExpectationsEvaluated` integer: the evaluation budget (500); present only when truncated is true
  - `narration` ExplanationNarration: present only with ?narrate=true: a plain-language summary of the explanation written by the configured LLM backend, or why none is available. It is AI-generated and can be wrong; the other fields are the deterministic facts
    - `available` boolean: whether the LLM returned a summary
    - `aiGenerated` boolean: always true when available; marks the text as written by an LLM, not by MockServer
    - `label` string: a label to show beside the text, saying it is AI-generated and can be wrong
    - `provider` string: the configured LLM provider that wrote the text
    - `model` string: the model that wrote the text, when known
    - `text` string: the summary, at most 4,000 characters including a trailing '...[truncated]' when it was cut
    - `reason` string: when not available, why: llmExplainNarrationEnabled is off, no LLM backend is configured, the LLM failed or returned no text, it did not answer within llmRequestTimeoutMillis plus a 2-second grace, or two narrations are already running

## Other responses

- `400`: no correlationId was supplied, or the recorded request is not an HTTP request
- `404`: no request is recorded with this correlationId (never received, cleared or evicted)

---

[API](https://skmtc.dev/mock-server/apis/mockserver-api.md) · [All operations](https://skmtc.dev/mock-server/apis/mockserver-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mock-server/apis/mockserver-api/revisions/9239b693996e?raw)
