laravel/framework · error · RuntimeException

SQLite does not support altering primary keys.

Error message

SQLite does not support altering primary keys.

What it means

SQLiteGrammar.compileRenameIndex() throws RuntimeException when the matched index is a PRIMARY KEY. SQLite's primary keys are tied to the table's rowid and cannot be altered via the drop+recreate rename path the grammar uses for normal indexes. This fires AFTER the index lookup succeeds but identifies it as primary.

Source

Thrown at src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php:647

     *
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @param  \Illuminate\Support\Fluent  $command
     * @return array
     *
     * @throws \RuntimeException
     */
    public function compileRenameIndex(Blueprint $blueprint, Fluent $command)
    {
        $indexes = $this->connection->getSchemaBuilder()->getIndexes($blueprint->getTable());

        $index = Arr::first($indexes, fn ($index) => $index['name'] === $command->from);

        if (! $index) {
            throw new RuntimeException("Index [{$command->from}] does not exist.");
        }

        if ($index['primary']) {
            throw new RuntimeException('SQLite does not support altering primary keys.');
        }

        if ($index['unique']) {
            return [
                $this->compileDropUnique($blueprint, new IndexDefinition(['index' => $index['name']])),
                $this->compileUnique($blueprint,
                    new IndexDefinition(['index' => $command->to, 'columns' => $index['columns']])
                ),
            ];
        }

        return [
            $this->compileDropIndex($blueprint, new IndexDefinition(['index' => $index['name']])),
            $this->compileIndex($blueprint,
                new IndexDefinition(['index' => $command->to, 'columns' => $index['columns']])
            ),
        ];
    }

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Do not rename a primary key index on SQLite — leave primary keys as-is or recreate the table with the new constraint.
  2. If a rename is essential, build a new table with the desired primary key, copy data, and swap (SQLite table-rebuild pattern).
  3. Skip the rename for SQLite via a driver guard and only rename on drivers that permit it.

Example fix

// before (throws on SQLite)
Schema::table('users', function (Blueprint $table) {
    $table->renameIndex('users_pkey', 'users_pk');
});

// after — skip on SQLite, rebuild table if rename is required
Schema::table('users', function (Blueprint $table) {
    if ($table->getConnection()->getDriverName() !== 'sqlite') {
        $table->renameIndex('users_pkey', 'users_pk');
    }
});
Defensive patterns

Strategy: validation

Validate before calling

if (Schema::getConnection()->getDriverName() !== 'sqlite') {
    $table->renameIndex('users_pkey', 'users_pk');
}

Prevention

When it happens

Trigger: Calling $table->renameIndex('users_pkey', 'users_id_new') (or the auto-generated primary index name) on a SQLite connection. The lookup finds the index but $index['primary'] is true, so SQLite refuses the rename.

Common situations: Renaming what is actually a primary key index; sharing rename migrations between drivers (Postgres/MySQL allow renaming primary indexes in some forms); schema-diff tools emitting primary-index renames.

Related errors


AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06). Data as JSON: /data/errors/9b52e753fe9d8804.json. Report an issue: GitHub.