{"record":{"id":"0773d9d928ddd9e6","repo":"laravel/framework","slug":"index-name-contains-invalid-characters-0773d9","errorCode":null,"errorMessage":"Index name contains invalid characters.","messagePattern":"Index name contains invalid characters\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Illuminate/Database/Query/Grammars/SQLiteGrammar.php","lineNumber":185,"sourceCode":"    /**\n     * Compile the index hints for the query.\n     *\n     * @param  \\Illuminate\\Database\\Query\\Builder  $query\n     * @param  \\Illuminate\\Database\\Query\\IndexHint  $indexHint\n     * @return string\n     *\n     * @throws \\InvalidArgumentException\n     */\n    protected function compileIndexHint(Builder $query, $indexHint)\n    {\n        if ($indexHint->type !== 'force') {\n            return '';\n        }\n\n        $index = $indexHint->index;\n\n        if (! preg_match('/^[a-zA-Z0-9_$]+$/', $index)) {\n            throw new InvalidArgumentException('Index name contains invalid characters.');\n        }\n\n        return \"indexed by {$index}\";\n    }\n\n    /**\n     * Compile a \"JSON length\" statement into SQL.\n     *\n     * @param  string  $column\n     * @param  string  $operator\n     * @param  string  $value\n     * @return string\n     */\n    protected function compileJsonLength($column, $operator, $value)\n    {\n        [$field, $path] = $this->wrapJsonFieldAndPath($column);\n\n        return 'json_array_length('.$field.$path.') '.$operator.' '.$value;","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/laravel/framework/blob/e0f6eb3518ac29fbbca8529e97d0df7fc9f24481/src/Illuminate/Database/Query/Grammars/SQLiteGrammar.php#L167-L203","documentation":"SQLiteGrammar's compileIndexHint only processes 'force' type hints (SQLite's INDEXED BY clause). It validates the index name against /^[a-zA-Z0-9_$]+$/ and throws if the name contains characters outside that set. Unlike MySQL, SQLite handles a single index (not comma-separated). 'hint' and 'ignore' types are silently ignored (return empty string).","triggerScenarios":"Calling ->forceIndex('bad name') or ->from('table', indexHint with type 'force' and invalid name) on a SQLite connection. Index names with spaces, hyphens, dots, or other special characters trigger the throw.","commonSituations":"Using forceIndex in code that runs on multiple database engines — the index name is valid on one engine but contains characters SQLite rejects. Running tests on SQLite with index hints designed for MySQL.","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 testing on SQLite, the index hint is only enforced for force type — ensure the index name matches SQLite naming rules"],"exampleFix":"// before — invalid characters in index name throws on SQLite\nDB::table('users')->forceIndex('users.email-lookup')->get();\n\n// after — valid index name passes\nDB::table('users')->forceIndex('users_email_lookup')->get();","handlingStrategy":"validation","validationCode":"$indexName = 'users_email_lookup';\nif (preg_match('/^[a-zA-Z0-9_$]+$/', $indexName)) {\n    DB::table('users')->forceIndex($indexName)->get();\n} else {\n    $sanitized = preg_replace('/[^a-zA-Z0-9_$]/', '_', $indexName);\n    DB::table('users')->forceIndex($sanitized)->get();\n}","typeGuard":"function isValidIndexName(string $name): bool {\n    return (bool) preg_match('/^[a-zA-Z0-9_$]+$/', $name);\n}","tryCatchPattern":"try {\n    DB::table('users')->forceIndex($indexName)->get();\n} catch (\\InvalidArgumentException $e) {\n    if (str_contains($e->getMessage(), 'invalid characters')) {\n        $sanitized = preg_replace('/[^a-zA-Z0-9_$]/', '_', $indexName);\n        DB::table('users')->forceIndex($sanitized)->get();\n    } else {\n        throw $e;\n    }\n}","preventionTips":["Use only alphanumeric and underscore characters in index names","On SQLite, note that only force-type hints produce SQL (hint/ignore are silently dropped)","Validate dynamic index names before passing to forceIndex on SQLite"],"tags":["index","sqlite","validation","grammar"],"backgroundTag":null,"analyzedSha":"e0f6eb3518ac29fbbca8529e97d0df7fc9f24481","analyzedAt":"2026-08-11T20:52:37.562Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}