TryGhost/Ghost · error · BadRequestError
The NQL filter you passed was invalid.
Error message
The NQL filter you passed was invalid.
What it means
Thrown by the tiers serializer `all` method when `nql.parse(frame.options.filter)` raises an exception. Ghost parses the supplied `filter` query string into an NQL AST before querying tiers; invalid syntax is caught and converted to a 400 BadRequestError with `invalidNQLFilter`.
Source
Thrown at ghost/core/core/server/api/endpoints/utils/serializers/input/tiers.js:64
converted.monthlyPrice = converted.monthly_price;
delete converted.monthly_price;
}
if (Reflect.has(converted, 'yearly_price')) {
converted.yearlyPrice = converted.yearly_price;
delete converted.yearly_price;
}
return converted;
}
module.exports = {
all(_apiConfig, frame) {
if (frame.options.filter) {
try {
frame.options.filter = nql.parse(frame.options.filter);
} catch (err) {
throw new BadRequestError({
message: tpl(messages.invalidNQLFilter)
});
}
} else {
frame.options.filter = null;
}
if (localUtils.isContentAPI(frame)) {
// CASE: content api can only have active tiers
forceActiveFilter(frame);
// CASE: content api includes these by default
const defaultRelations = ['monthly_price', 'yearly_price', 'benefits'];
if (!frame.options.withRelated) {
frame.options.withRelated = defaultRelations;
} else {
for (const relation of defaultRelations) {
if (!frame.options.withRelated.includes(relation)) {View on GitHub (pinned to 47d8b0e2ad)
Solutions
- Validate/escape the filter string client-side before sending the request.
- Test the filter against NQL syntax documentation; balance parentheses and quotes.
- Use only supported NQL operators and field names for tiers.
- Simplify the filter incrementally to find the offending token.
Example fix
// before
await api.tiers.browse({filter: "status:'active"}); // unbalanced quote
// after
await api.tiers.browse({filter: 'status:active'}); Defensive patterns
Strategy: validation
Validate before calling
import nql from '@tryghost/nql';
function isValidFilter(filter: string): boolean {
try { nql.parse(filter); return true; } catch { return false; }
} Try / catch
try {
await api.tiers.browse({filter});
} catch (err) {
if (/invalid NQL filter/i.test(JSON.stringify((err as any).response?.body))) {
// simplify/escape the filter and retry
}
throw err;
} Prevention
- Validate NQL filter syntax client-side before sending.
- Balance parentheses and quotes in filter expressions.
- Use only NQL-supported operators and valid tier field names.
When it happens
Trigger: A GET request to the tiers API includes a `?filter=` query with syntactically invalid NQL (unbalanced parentheses, unknown operators, bad field references that the parser rejects), and `nql.parse` throws.
Common situations: Typo in filter syntax (e.g., `status:active'`), mismatched quotes/parentheses, using SQL-style operators not supported by NQL, passing a raw user-provided string unescaped, or a client library generating malformed filters after an upgrade.
Related errors
- Cannot disconnect Stripe whilst you have active subscription
- Failed to convert HTML to Lexical
- Failed to convert HTML to Lexical
- Subject is required
- Email content is required
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/737a4fb71aabc059.
Report an issue: GitHub.