laravel/framework · error · InvalidArgumentException
Index name contains invalid characters.
Error message
Index name contains invalid characters.
What it means
SqlServerGrammar's compileIndexHint only processes 'force' type hints (SQL Server's WITH (INDEX(...)) clause). It validates the index name against /^[a-zA-Z0-9_$]+$/ and throws if the name contains characters outside that set. 'hint' and 'ignore' types are silently ignored (return empty string).
Solutions
- Rename the index in the migration to use only alphanumeric, underscore, and dollar-sign characters
- Remove invalid characters from the index name string
- If the index truly requires special characters, use a raw SQL hint via fromRaw() instead of the index hint API
Example fix
// before — dot in index name throws on SQL Server
DB::table('orders')->forceIndex('dbo.order_idx')->get();
// after — valid name without dots
DB::table('orders')->forceIndex('order_idx')->get(); Defensive patterns
Strategy: validation
Validate before calling
$indexName = 'order_idx';
if (preg_match('/^[a-zA-Z0-9_$]+$/', $indexName)) {
DB::table('orders')->forceIndex($indexName)->get();
} else {
$sanitized = preg_replace('/[^a-zA-Z0-9_$]/', '_', $indexName);
DB::table('orders')->forceIndex($sanitized)->get();
} Type guard
function isValidIndexName(string $name): bool {
return (bool) preg_match('/^[a-zA-Z0-9_$]+$/', $name);
} Try / catch
try {
DB::table('orders')->forceIndex($indexName)->get();
} catch (\InvalidArgumentException $e) {
if (str_contains($e->getMessage(), 'invalid characters')) {
$sanitized = preg_replace('/[^a-zA-Z0-9_$]/', '_', $indexName);
DB::table('orders')->forceIndex($sanitized)->get();
} else {
throw $e;
}
} Prevention
- Use only alphanumeric and underscore characters in index names on SQL Server
- On SQL Server, note that only force-type hints produce SQL (hint/ignore are silently dropped)
- Avoid schema-qualified names (schema.index) in forceIndex calls — use bare names
When it happens
Trigger: Calling ->forceIndex('bad name') or ->from('table', indexHint with type 'force' and invalid name) on a SQL Server (sqlsrv) connection. Index names with spaces, hyphens, dots, or other special characters trigger the throw.
Common situations: Using forceIndex in a multi-database application where the index name is valid on MySQL but rejected by SQL Server's stricter validation. Passing schema-qualified index names with dots.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Index name contains invalid characters.
- Index name contains invalid characters.
- Add fillable property
- Collection given to whereAttachedTo method may not be empty.
- Collection given to whereBelongsTo method may not be empty.
AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11).
Data as JSON: /api/errors/766d95b207a3032d.
Report an issue: GitHub.
Appendix: source
Thrown at src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php:126
/**
* Compile the index hints for the query.
*
* @param \Illuminate\Database\Query\Builder $query
* @param \Illuminate\Database\Query\IndexHint $indexHint
* @return string
*
* @throws \InvalidArgumentException
*/
protected function compileIndexHint(Builder $query, $indexHint)
{
if ($indexHint->type !== 'force') {
return '';
}
$index = $indexHint->index;
if (! preg_match('/^[a-zA-Z0-9_$]+$/', $index)) {
throw new InvalidArgumentException('Index name contains invalid characters.');
}
return "with (index([{$index}]))";
}
/**
* {@inheritdoc}
*
* @param \Illuminate\Database\Query\Builder $query
* @param array $where
* @return string
*/
protected function whereBitwise(Builder $query, $where)
{
$value = $this->parameter($where['value']);
$operator = str_replace('?', '??', $where['operator']);
View on GitHub (pinned to e0f6eb3518)