INVALID_FIELD: <field> is not filterable
You used a field in `WHERE`, `ORDER BY`, or `GROUP BY` that the platform refuses to filter or sort on — almost always a Long Text Area, Rich Text, or Encrypted field. The fix is either using a different field for the filter, or moving to SOSL for text searches.
Also seen asfield is not filterable·field is not sortable·INVALID_FIELD: is not filterable·Field cannot be used in a WHERE
A reporting service queries the Knowledge object to surface articles that match a customer's case. The query has been working in development. The first call in production fails with INVALID_FIELD: Body is not filterable. The developer is surprised: the field is on the object, it has data, and the SELECT clause references it without trouble. The problem only appears when the field moves into the WHERE clause.
What the platform is checking
Salesforce marks certain fields as not filterable. A non-filterable field can appear in the SELECT clause and the result set, but it cannot appear in WHERE, ORDER BY, GROUP BY, or any other clause that requires the platform to index or compare the values. Long text areas, rich text areas, encrypted fields, formula fields with certain return types, and some standard fields (like Body on Note, Description on Solution, or ArticleBody on Knowledge) all carry this restriction.
The constraint exists because filtering and sorting large text content is expensive. A WHERE clause runs against indexed columns, and Salesforce does not maintain a filterable index for long text. SOSL exists precisely to search large text content. SOQL handles structured fields. The boundary between them is enforced at parse time, before the query even runs.
The error fires at SOQL parsing, not at execution. A query that names a non-filterable field in any non-SELECT position is rejected with INVALID_FIELD: <fieldname> is not filterable and the request returns immediately. No rows are scanned, no records are returned.
The broken example
A controller that finds Knowledge articles matching a Case description:
public class CaseKnowledgeMatcher {
@AuraEnabled(cacheable=true)
public static List<Knowledge__kav> findMatches(Id caseId) {
Case c = [SELECT Id, Subject, Description FROM Case WHERE Id = :caseId];
String keyword = '%' + c.Subject + '%';
return [
SELECT Id, Title, ArticleBody
FROM Knowledge__kav
WHERE PublishStatus = 'Online'
AND Language = 'en_US'
AND ArticleBody LIKE :keyword
LIMIT 10
];
}
}
The query attempts to filter on ArticleBody. The field is a rich text area on the Knowledge object. The parser rejects the query with INVALID_FIELD: ArticleBody is not filterable. The page shows an error toast, and the agent sees no suggested articles.
A second shape: an Opportunity query that filters on the Description field:
[
SELECT Id, Name
FROM Opportunity
WHERE Description LIKE '%urgent%'
]
Description on Opportunity is a long text area. The same rule applies: the field is not filterable, and the query fails.
A third shape: a report query that tries to order by a long text field:
[
SELECT Id, Subject
FROM Case
ORDER BY Description
LIMIT 100
]
ORDER BY also requires indexed columns. The Description field cannot satisfy that constraint, and the parser rejects the ORDER BY clause for the same reason.
The fix, three paths
Use SOSL when the intent is text search. SOSL searches text content across one or more objects. It is designed for the exact use case where SOQL fails: matching content inside long text or rich text fields.
public static List<Knowledge__kav> findMatches(Id caseId) {
Case c = [SELECT Id, Subject FROM Case WHERE Id = :caseId];
String keyword = c.Subject;
List<List<SObject>> results = [
FIND :keyword
IN ALL FIELDS
RETURNING Knowledge__kav(Id, Title, ArticleBody
WHERE PublishStatus = 'Online'
AND Language = 'en_US'
LIMIT 10)
];
return (List<Knowledge__kav>) results[0];
}
SOSL accepts a search term and returns matching records. The WHERE clause inside RETURNING applies to filterable fields. The text match itself runs through Salesforce's full-text search index, which is the right tool for matching large text content.
Filter on a related filterable field instead. When the data model includes a derived field that captures the same intent, filtering on the derived field works while still returning the long text. A Knowledge article often has tags, categories, or summary fields that are filterable:
[
SELECT Id, Title, ArticleBody
FROM Knowledge__kav
WHERE PublishStatus = 'Online'
AND Language = 'en_US'
AND Tag__c LIKE :keyword
LIMIT 10
]
The query returns the long text but filters on a structured field. If the data model does not yet have a filterable proxy, adding a text field (length up to 255) that captures a summary or tag of the long content enables filtering for queries that need it.
Filter in Apex after returning the rows. When neither SOSL nor a filterable proxy is suitable, the fallback is to query without the filter and apply the filter in code:
List<Knowledge__kav> all = [
SELECT Id, Title, ArticleBody
FROM Knowledge__kav
WHERE PublishStatus = 'Online'
AND Language = 'en_US'
LIMIT 1000
];
List<Knowledge__kav> matches = new List<Knowledge__kav>();
for (Knowledge__kav k : all) {
if (k.ArticleBody != null && k.ArticleBody.containsIgnoreCase(keyword)) {
matches.add(k);
if (matches.size() == 10) break;
}
}
This pattern only scales when the unfiltered query returns a manageable number of rows. Returning 1,000 articles to find 10 matches is wasteful but workable; returning 100,000 rows is not. The query governors (50,000 rows, heap size, CPU time) put a hard cap on this approach.
The fixed example
The controller rewritten with SOSL:
public class CaseKnowledgeMatcher {
@AuraEnabled(cacheable=true)
public static List<Knowledge__kav> findMatches(Id caseId) {
if (caseId == null) return new List<Knowledge__kav>();
Case c = [SELECT Id, Subject FROM Case WHERE Id = :caseId LIMIT 1];
if (String.isBlank(c.Subject)) {
return new List<Knowledge__kav>();
}
String searchTerm = c.Subject.replaceAll('[^a-zA-Z0-9 ]', ' ');
if (String.isBlank(searchTerm)) {
return new List<Knowledge__kav>();
}
List<List<SObject>> results = [
FIND :searchTerm
IN ALL FIELDS
RETURNING Knowledge__kav(Id, Title, Summary
WHERE PublishStatus = 'Online'
AND Language = 'en_US'
ORDER BY LastPublishedDate DESC
LIMIT 10)
];
return (List<Knowledge__kav>) results[0];
}
}
The method sanitizes the search term, calls SOSL for the text match, and applies SOQL-side filters for publish status and language. The result is the right shape with no parser errors.
Edge case: rich text versus long text versus formula
Three field types are commonly hit by this rule.
Long Text Area fields hold up to 131,072 characters. They are never filterable. Examples: Case.Description, Opportunity.Description, Account.Description.
Rich Text Area fields hold HTML up to a configurable length. They are not filterable. Examples: Knowledge__kav.ArticleBody, Email Templates' HtmlValue.
Formula fields that return Text type are filterable when the formula references only filterable fields. A formula that references a long text area inherits the non-filterable property. Reading the formula's referenced fields tells you whether the formula itself is filterable.
Encrypted Text fields with Classic Encryption are not filterable except for the first 4 characters in some configurations. With Shield Platform Encryption, they are filterable in equality checks but not in LIKE clauses.
The pattern: anything that holds arbitrary user content longer than 255 characters, or any field where the underlying storage is not an indexed text column, falls under the non-filterable rule.
Edge case: formula fields and roll-up summaries
A formula field that returns a number, date, or short text is usually filterable. A formula that returns a long text value or includes a cross-object reference to a long text field becomes non-filterable. The salesforce describe API exposes the filterable property on every field. A defensive Apex utility reads the describe before constructing the query:
public static Boolean isFilterable(String objectName, String fieldName) {
Schema.DescribeSObjectResult d = Schema.getGlobalDescribe()
.get(objectName).getDescribe();
Schema.DescribeFieldResult f = d.fields.getMap().get(fieldName).getDescribe();
return f.isFilterable();
}
A query builder can call this before adding the field to the WHERE clause and pick a fallback path when the field is non-filterable.
Edge case: SOQL through the REST API
The same constraint applies whether the query runs through Apex, the SOAP API, the REST API, or the Bulk API. The parser is the same. A client integration that worked in development against a sandbox without the field populated will fail in production against the same query if the field is non-filterable.
Edge case: groupable, sortable, and aggregatable properties
Beyond filterable, the field describe also exposes groupable, sortable, and aggregatable. A field that is filterable may still not be groupable (cannot appear in GROUP BY) or sortable (cannot appear in ORDER BY). A report or query that groups or sorts by a non-groupable field receives a similar INVALID_FIELD error.
Schema.DescribeFieldResult f = Schema.SObjectType.Account.fields.getMap()
.get('Description').getDescribe();
System.debug('Filterable: ' + f.isFilterable());
System.debug('Groupable: ' + f.isGroupable());
System.debug('Sortable: ' + f.isSortable());
Each property is independent. A field can be filterable but not groupable (a date-time field, for example), or groupable but not sortable in some edge cases. Reading the describe at query-build time gives the safest signal.
Edge case: encrypted fields with Shield Platform Encryption
Shield-encrypted fields have nuanced filterability. Equality filters on encrypted fields work; LIKE filters do not (the encrypted value does not preserve substrings). Probabilistic encryption preserves equality and inequality but breaks LIKE; deterministic encryption preserves equality only. Choosing the right encryption mode at field creation determines what filters will work later.
Edge case: indexed long text via External Search
For genuine text-search needs against long text content, External Search Connector or Salesforce Connect with an external search service can index the text outside the platform and surface results through SOSL or through custom UI. The architecture is heavier but handles content that does not fit the SOQL or SOSL model cleanly.
Defensive habits
Treat long text areas, rich text areas, and any field with "Description" or "Body" or "Notes" in its name as suspect. If you find yourself filtering on one, stop and consider SOSL or a filterable proxy.
Use the describe API to discover filterability at runtime. Hardcoding field names in a query builder works until the data model changes. Reading filterability from describe makes the code self-correcting.
Define a search service that wraps SOSL behind a clean interface. Application code calls SearchService.findArticles(keyword); the service knows whether to use SOSL or SOQL depending on the field shape. Centralizing this decision keeps query knowledge in one place.
Add a thin text field to long-text-heavy objects when filtering matters. A 255-character "Summary__c" or "Tags__c" field that the user populates manually or that a formula populates from the long text is filterable and gives the data model an entry point for queries.
Test patterns
A test that exercises both the success path and the empty-search path:
@IsTest
static void findMatchesReturnsArticlesForMatchingSubject() {
Case c = new Case(Subject='Login fails after MFA reset', Status='New', Origin='Web');
insert c;
Test.startTest();
List<Knowledge__kav> result = CaseKnowledgeMatcher.findMatches(c.Id);
Test.stopTest();
System.assertNotEquals(null, result);
}
@IsTest
static void findMatchesReturnsEmptyForBlankSubject() {
Case c = new Case(Subject=null, Status='New', Origin='Web');
insert c;
Test.startTest();
List<Knowledge__kav> result = CaseKnowledgeMatcher.findMatches(c.Id);
Test.stopTest();
System.assertEquals(0, result.size());
}
SOSL in tests requires Test.setFixedSearchResults to return deterministic results, so a production-faithful test of the matching itself looks slightly different. The above tests cover the entry-point contract.
Diagnosing in production
When the error fires:
- Read the field name from the error message.
- Find the query that references the field in a non-SELECT clause.
- Check the field's
Filterableproperty via Setup or the describe API. - Pick the right fix: SOSL for content search, a filterable proxy field for structured matching, or in-memory filtering for small result sets.
- Deploy and confirm the query parses cleanly.
The error is precise and the diagnosis is fast. Most occurrences resolve within the same hour as the failure.
Anti-pattern: the partial-string workaround
Some developers try to copy the first 255 characters of a long text field into a separate text field, then filter on the copy. This works for queries that match within the first 255 characters but silently misses matches beyond. The integration that depends on the filter quietly returns wrong results.
A safer pattern uses a manually maintained tag field. The user (or an LLM in a flow) populates 3-5 keywords describing the content. The tags are filterable and the matching is intentional rather than positional.
Quick recovery checklist
- Read the error and find the field name.
- Confirm the field is non-filterable via describe or Setup.
- Choose SOSL, a proxy field, or in-memory filtering.
- Rewrite the query.
- Add a regression test.
- Deploy.
The class of bug is bounded. Once the developer has internalized the filterability rule, future queries get the right shape on the first try.
Further reading from Salesforce
- SOQL and SOSL Reference: SOSL Syntax
- SOQL and SOSL Reference: WHERE Condition Expressions
- Apex Developer Guide: Dynamic SOQL and Describe
- Architect: Data Architecture and Management
- Trailhead: Search Solution Basics
Related dictionary terms
Share this fix
Related SOQL errors
INVALID_FIELD: No such column 'X' on entity 'Y'
SOQLEither the field truly doesn't exist on the object, or it exists but the running user / API session can't see it. The same error message is …
INVALID_FIELD: NULL_FOR_NON_REFERENCE_FIELD: <field> can not be null
SOQLYou assigned `null` to a field type that doesn't accept null — usually a Boolean (which expects `true` or `false`, not null) or a primitive …
INVALID_SEARCH: search term must be longer than one character
SOQLA SOSL `FIND` expression had a search term shorter than two characters, or used wildcards in a way the parser rejects. SOSL has stricter rul…
MALFORMED_QUERY: unexpected token
SOQLThe SOQL parser couldn't make sense of your query. The error message tells you the exact word it choked on — that's where the syntax broke. …
OPERATION_TOO_LARGE: Aggregate query has too many rows for direct assignment, use FOR loop
SOQLAn aggregate or relationship query returned more rows than Apex will assign to a `List<AggregateResult>` variable in one go. The platform te…