
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeI've seen this issue before! A 400 error with "invalid character" in Sanity search is usually caused by special characters in your search input that aren't properly handled when building GROQ queries. Let me walk you through what's happening and how to fix it.
When you're building search queries dynamically (especially with the match operator), characters like quotes (" or '), backslashes (\), or other special characters can break your GROQ query syntax if you're using string interpolation:
// ❌ This breaks if searchTerm contains quotes or special chars
const query = `*[_type == "post" && title match "${searchTerm}"]`
// User types: demo"test
// Results in: *[_type == "post" && title match "demo"test"]
// → 400 error: invalid characterThe best and safest approach is to use GROQ parameters instead of string interpolation. Parameters are automatically escaped by the Sanity client, preventing syntax errors:
// ✅ Safe approach using parameters
const query = `*[_type == "post" && title match $searchTerm]`
const params = { searchTerm: `*${userInput}*` }
const results = await client.fetch(query, params)With parameters, the Sanity client handles all the escaping for you, so special characters won't break your query.
match OperatorOne important thing to know: the match operator works on tokenized text, not regex patterns. It breaks text into searchable words based on word boundaries and special characters. For example, "user@sanity.io" is tokenized as ['user', 'sanity', 'io'].
This means:
match to work correctly@, ., / act as word separators*) are supported for prefix matchingHere's a robust implementation:
import { client } from './sanityClient'
async function search(userInput) {
const sanitized = userInput.trim()
if (!sanitized) return []
// Using parameters (recommended)
const query = `*[_type == "post" && title match $searchTerm] | order(_createdAt desc)`
const params = { searchTerm: `*${sanitized}*` }
try {
return await client.fetch(query, params)
} catch (error) {
console.error('Search error:', error)
return []
}
}${}, switch to GROQ parameters with $variableNameIf you're calling the HTTP API directly (not through the JavaScript client), make sure your query is properly URL-encoded:
const encodedQuery = encodeURIComponent(query)The JavaScript client handles this automatically, which is another reason to use parameters!
Let me know if you're still seeing the 400 error after switching to parameters - it would help to see the exact query that's failing in your Network tab. The error message should also show which character is causing the issue.
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.
Content operations
Content backend


The only platform powering content operations
By Industry


Tecovas strengthens their customer connections
Build and Share

Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag store