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.
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
A failed claim and a refused claim both throw Geo, 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.
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();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)refused GEOSPEC_UNSUPPORTED_EVIDENCEThe 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.
const part = await loadModel({ source: 'involute-gear.glb', format: 'glb', sourceUnit: 'mm' });Tab indents. Press Escape, then Tab, to leave the editor.
The spec as a file, with the model loaded over the native addon
import { readFile } from 'node:fs/promises';import { expectGeo } from 'geospec';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 part = await loadModel({ source: 'involute-gear.glb', format: 'glb', sourceUnit: 'mm' });expectGeo(part) .toHaveCircularHole({ diameter: 4 });await loadModel.dispose();engine.close();involute-gear.glb · 52 KB · copied from an engine test fixture
Run the spec to see what the engine answers for each claim. Then change an expected value and run it again.
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_ | 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 says where it lives. |
GEOSPEC_ | A pairs entry of an interference claim did not match two distinct components. | Use the component names the file declares. |
GEOSPEC_ | 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_ | 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_ | 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_: a query asserts nothing, so a selector it cannot resolve leaves it without evidence rather than failing it. The cheat sheet lists each capability's own refusal codes, and the matcher reference the ones every capability shares.
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_TIMEOUTis 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_andANALYSIS_ LIMIT GEOSPEC_name a host limit, raised where the engine is constructed, or a query limit such asRESOURCE_ LIMIT maxRecords; a capacity a proof profile fixes is raised by no setting, and the diagnostic'ssuggestionsays which. Set tolerances and budgets 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
A refusal inside a pair claim. to and to evaluate several pairs. One pair the engine cannot decide refuses the whole claim, so a selector problem hides every other answer. Compare parts 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 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_. The matcher reference covers the query.
Set tolerances and budgets
State how close a measurement must come, compare with strict bounds, and set the work and the memory a claim may spend before it is refused.
Reproduce a result
Keep the bytes a result depends on, hash the result bytes the engine wrote, and get the same digest from TypeScript and Python.