typeorm/typeorm · error · TypeORMError

Supplied index ${indexOrName} was not found in table ${table

Error message

Supplied index ${indexOrName} was not found in table ${table.name}

What it means

Thrown by SpannerQueryRunner.dropIndex when the supplied index (by TableIndex object or by name string) is not present in the cached table's `indices` array. Suppressed when ifExists:true is passed, which is the idiomatic way to drop indexes that may not exist.

Source

Thrown at src/driver/spanner/SpannerQueryRunner.ts:1680

     * @param indexOrName
     * @param ifExists
     */
    async dropIndex(
        tableOrName: Table | string,
        indexOrName: TableIndex | string,
        ifExists?: boolean,
    ): Promise<void> {
        const table =
            tableOrName instanceof Table
                ? tableOrName
                : await this.getCachedTable(tableOrName)
        const index =
            indexOrName instanceof TableIndex
                ? indexOrName
                : table.indices.find((i) => i.name === indexOrName)
        if (!index) {
            if (ifExists) return
            throw new TypeORMError(
                `Supplied index ${indexOrName} was not found in table ${table.name}`,
            )
        }

        // new index may be passed without name. In this case we generate index name manually.
        index.name ??= this.generateIndexName(table, index)

        const up = this.dropIndexSql(table, index)
        const down = this.createIndexSql(table, index)
        await this.executeQueries(up, down)
        table.removeIndex(index)
    }

    /**
     * Drops an indices from the table.
     *
     * @param tableOrName
     * @param indices

View on GitHub (pinned to 04ff4daedc)

Solutions

  1. Pass ifExists:true: `await qr.dropIndex('user', 'idx_user_email', true)`.
  2. Check existence first via getTable and the indices array before dropping.
  3. Confirm the actual index name from INFORMATION_SCHEMA.INDEXES on spanner (`INDEX_NAME`) and align your migration.
  4. Use up/down idempotent migration patterns so partial replays don't leave stale drop steps.

Example fix

// before
await queryRunner.dropIndex('user', 'idx_user_email')

// after
await queryRunner.dropIndex('user', 'idx_user_email', true)
// or
const t = await queryRunner.getTable('user')
if (t?.indices.some(i => i.name === 'idx_user_email')) {
  await queryRunner.dropIndex(t!, 'idx_user_email')
}
Defensive patterns

Strategy: validation

Validate before calling

async function safeDropIndex(qr: QueryRunner, tableName: string, name: string) {
  await qr.dropIndex(tableName, name, true) // ifExists
}

Type guard

function indexExists(table: Table, name: string): boolean {
  return table.indices.some(i => i.name === name)
}

Try / catch

try {
  await qr.dropIndex('user', 'idx_email')
} catch (e) {
  if (e instanceof TypeORMError && /was not found in table/.test(e.message)) return
  throw e
}

Prevention

When it happens

Trigger: Calling dropIndex(table, 'idx_user_email') where the index was never created, already dropped, or named differently. Common when migrations run partially or when an index name was changed by a naming-strategy update.

Common situations: Re-running a migration after a partial failure; deploying to an environment whose schema drifted; renaming indexes via NamingStrategy changes; concurrency where another process dropped the index first.

Related errors


AI-assisted analysis of typeorm/typeorm@04ff4daedc (2026-08-03). Data as JSON: /data/errors/9d478e50fc8f09ca.json. Report an issue: GitHub.