avajs/ava · error · AssertionError
`t.like()` selector must not contain circular references
Error message
`t.like()` selector must not contain circular references
What it means
This AVA assertion error is thrown by t.like() when the selector object contains circular references. t.like() walks the selector via selectComparable() to pick the comparable subset of actual; a circular selector makes that walk impossible, so the library detects it (CIRCULAR_SELECTOR sentinel) and throws a fixed usage message instead of letting the walk blow the stack. The selector must be an acyclic plain object.
Source
Thrown at lib/assert.js:376
return pass();
});
this.like = withSkip((actual, selector, message) => {
assertMessage(message, 't.like()');
if (!isLikeSelector(selector)) {
throw fail(new AssertionError('`t.like()` selector must be a non-empty object', {
assertion: 't.like()',
formattedDetails: [formatWithLabel('Called with:', selector)],
}));
}
let comparable;
try {
comparable = selectComparable(actual, selector);
} catch (error) {
if (error === CIRCULAR_SELECTOR) {
throw fail(new AssertionError('`t.like()` selector must not contain circular references', {
assertion: 't.like()',
formattedDetails: [formatWithLabel('Called with:', selector)],
}));
}
throw error;
}
const result = concordance.compare(comparable, selector, concordanceOptions);
if (result.pass) {
return pass();
}
const actualDescriptor = result.actual ?? concordance.describe(comparable, concordanceOptions);
const expectedDescriptor = result.expected ?? concordance.describe(selector, concordanceOptions);
throw fail(new AssertionError(message, {
assertion: 't.like()',
formattedDetails: [formatDescriptorDiff(actualDescriptor, expectedDescriptor)],View on GitHub (pinned to bbfd946322)
Solutions
- Inspect the selector printed under 'Called with:' and remove or restructure the self-referencing property.
- If you need to assert on a circular actual value, pass only the non-cyclic subset as the selector.
- Restructure the selector as a fresh literal containing only the fields you want matched.
Example fix
// before
const selector = { name: 'x' };
selector.parent = selector; // circular
t.like(value, selector);
// after
const selector = { name: 'x' };
t.like(value, selector); Defensive patterns
Strategy: validation
Validate before calling
function hasCircular(obj, seen = new Set()) {
if (obj === null || typeof obj !== 'object') return false;
if (seen.has(obj)) return true;
seen.add(obj);
return Object.values(obj).some(v => hasCircular(v, seen));
}
// before calling: if (hasCircular(selector)) throw new Error('selector is circular'); Type guard
const isAcyclicSelector = (s) => typeof s === 'object' && s !== null && !hasCircular(s);
Try / catch
try {
t.like(actual, selector);
} catch (err) {
if (/circular references/.test(err.message)) {
t.fail('selector passed to t.like() is circular: ' + describe(selector));
} else { throw err; }
} Prevention
- Build selectors as fresh object literals, never mutated self-referencing objects.
- Run a cycle-detecting helper (e.g. hasCircular) on dynamically built selectors.
- Avoid copying objects with parent/back-pointers into selectors.
When it happens
Trigger: Calling t.like(actual, selector) where the selector (or a nested value inside it) references itself directly or indirectly, e.g. const sel = {a: 1}; sel.self = sel; t.like(obj, sel).
Common situations: Building the selector by mutation where a property is accidentally assigned back the selector itself; reusing an object that another object points back to; copying config objects that contain parent links.
Related errors
- `t.throws()` must be called with a function
- (validateExpectations error for t.throws())
- (validateExpectations error for t.throwsAsync())
- `t.throwsAsync()` must be called with a function or promise
- The `any` property of the second argument to `${assertion}`
AI-assisted analysis of avajs/ava@bbfd946322 (2026-09-02).
Data as JSON: /api/errors/01e5a7f81824d3cc.
Report an issue: GitHub.