# Read a refusal

Tell a refused claim from a failed one, find the code, and fix the model or the subject rather than the tolerance.

Canonical page: https://www.geospec.xyz/docs/guides/read-a-refusal

A claim has three outcomes. **Passed** and **failed** are answers about the geometry. **Refused** means the engine could not decide the claim on the geometry it was given, and says why with a typed diagnostic.

A refusal is never a pass. Negating a claim does not turn a refusal into a pass either: evidence the subject lacks is lacking under `.not` and `.not_` alike.

### 1. Catch the refusal [#1-catch-the-refusal]

A failed claim and a refused claim both throw `GeoSpecAssertionError`, which carries the whole report. Read the status first, then the first diagnostic: its code names the reason, its message what the claim needed, and its `suggestion`, where present, the change.

**TypeScript**

```ts title="check.ts"
import { readFile } from 'node:fs/promises';
import { expectGeo } from 'geospec';
import { GeoSpecAssertionError } from 'geospec/assertion-client';
import { createModelLoader } from 'geospec/model';
import { Engine } from 'geospec-engine/node';

const engine = new Engine();
const loadModel = createModelLoader({
  engine,
  readSource: async (source) => new Uint8Array(await readFile(source as string)),
});
const gear = await loadModel({ source: 'involute-gear.glb', format: 'glb', sourceUnit: 'mm' });

try {
  expectGeo(gear).toHaveCircularHole({ diameter: 4 });
} catch (error) {
  if (!(error instanceof GeoSpecAssertionError)) throw error;
  console.log(error.report.status, error.diagnostics[0].code);
}
await loadModel.dispose();
engine.close();
```

**Python**

```python title="check.py"
from geospec import GeoSpecAssertionError, GeoSpecEngine, expect_geo, load_model

with GeoSpecEngine(model_root="."):
    gear = load_model(file="involute-gear.glb", format="glb", source_unit="mm")
    try:
        expect_geo(gear).to_have_circular_hole(diameter=4)
    except GeoSpecAssertionError as error:
        print(error.result["status"], error.code)
```

```console
refused GEOSPEC_UNSUPPORTED_EVIDENCE
```

The spec below asks the same of the gear, read as GLB triangles: the mesh carries no circular-hole evidence, so the claim is refused, and its message says what was needed. Negate it and it is still refused. Switch to the STEP plate and the engine answers: the claim fails until `diameter` is the plate's 10 mm bore.

```ts
expectGeo(part)
  .toHaveCircularHole({ diameter: 4 });
```

### 2. Match the code to a change [#2-match-the-code-to-a-change]

Every code names the subject, the selectors, the budget or a limit, never the tolerance.

| Code                                    | Cause                                                                                                                                                                                                                                                                        | What to change                                                                                                                                               |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `GEOSPEC_UNSUPPORTED_EVIDENCE`          | The subject lacks the evidence the claim reads: a mesh asked for a hole, a mass claim with no `density` to derive it from, a wall-thickness claim on an unqualified shape, or `triangleCount` on a STEP solid, which would measure GeoSpec's own tessellation, not the part. | Admit the STEP export, declare what the claim derives from, or claim what the subject answers: `watertight` on the STEP export, triangle counts on the mesh. |
| `MATCHER_TIMEOUT`                       | The claim spent its work budget before it could decide.                                                                                                                                                                                                                      | Raise the budget for that claim; [Set tolerances and budgets](https://www.geospec.xyz/docs/guides/set-tolerances-and-budgets.mdx) says where it lives.                                  |
| `GEOSPEC_SELECTOR_UNMATCHED`            | A `pairs` entry of an interference claim did not match two distinct components.                                                                                                                                                                                              | Use the component names the file declares.                                                                                                                   |
| `GEOSPEC_SELECTOR_UNSUPPORTED_EVIDENCE` | A relationship claim selects by interface names the export does not carry.                                                                                                                                                                                                   | Author the interface names and re-export, or select with a derived query.                                                                                    |
| `GEOSPEC_ANALYSIS_LIMIT`                | The analysis exceeds a limit: the engine's `maxMeshBytes`, a `queryPmi` inventory over `maxRecords` or `maxOutputBytes`, or a capacity a proof profile fixes.                                                                                                                | Raise the host limit where the engine is constructed, or the query's limit; a fixed capacity is raised by no setting.                                        |
| `GEOSPEC_RESOURCE_LIMIT`                | Evidence derived for the claim, such as a STEP solid's tessellation, exceeds an admission limit: `maxTriangles`, `maxVertices` or `maxOccurrences`.                                                                                                                          | Construct the engine with a larger limit, or admit a smaller export.                                                                                         |

On `inspectGeometry` a selector that matches nothing refuses the query with `GEOSPEC_SELECTOR_UNMATCHED`: a query asserts nothing, so a selector it cannot resolve leaves it without evidence rather than failing it. The [cheat sheet](https://www.geospec.xyz/docs/reference/cheat-sheet.mdx) lists each capability's own refusal codes, and the [matcher reference](https://www.geospec.xyz/docs/reference/overview.mdx#common-refusals) the ones every capability shares.

### 3. Decide what to change [#3-decide-what-to-change]

* **The evidence is missing.** Feature claims such as circular holes need BRep evidence, which a triangle mesh does not carry: admit the STEP export of the same part, or claim what a mesh can answer, its bounds or whether it is watertight.
* **The claim is outside the admitted geometry.** Each reference page lists the geometry a capability admits; a claim outside it refuses, and no setting admits it.
* **A limit ran out.** `MATCHER_TIMEOUT` is the one refusal a spec setting resolves: the claim is admissible and ran out of work units. Raise the budget for that claim and leave the tolerance where it is. `GEOSPEC_ANALYSIS_LIMIT` and `GEOSPEC_RESOURCE_LIMIT` name a host limit, raised where the engine is constructed, or a query limit such as `maxRecords`; a capacity a proof profile fixes is raised by no setting, and the diagnostic's `suggestion` says which. [Set tolerances and budgets](https://www.geospec.xyz/docs/guides/set-tolerances-and-budgets.mdx#host-limits) shows a host limit raised.

Fix the model or the subject, never the tolerance: a refusal is a question asked of the wrong evidence, and a wider tolerance supplies none. A test that accepts a refusal as a pass passes on geometry nobody measured.

## Variations [#variations]

**A refusal inside a pair claim.** `toHaveSpatialRelationships` and `toHaveNoComponentInterference` evaluate several pairs. One pair the engine cannot decide refuses the whole claim, so a selector problem hides every other answer. [Compare parts](https://www.geospec.xyz/docs/guides/compare-parts.mdx) covers pair claims.

**A refusal in a test run.** Under vitest or pytest a refused claim throws like a failed one, so the test fails either way; under `geospec run` its test is `inconclusive`. A test that expects the refusal reads the status and the code from the error. [Test parts in CI](https://www.geospec.xyz/docs/guides/test-parts-in-ci.mdx) shows each.

**`minimumDistance`.** TypeScript's `client.query` answers it with its own result instead of throwing: without AP242 provenance, `status` is `refused` and `code` is `unsupported-evidence`; Python's `query_geo` returns the report, `refused` with `GEOSPEC_UNSUPPORTED_EVIDENCE`. The [matcher reference](https://www.geospec.xyz/docs/reference/queries.mdx#minimumdistance) covers the query.