laravel/framework · error · InvalidArgumentException

The unique columns must not be empty.

Error message

The unique columns must not be empty.

What it means

Thrown by insertOrIgnoreReturning when $uniqueBy is either an empty array `[]` or an empty string ''. The uniqueBy columns define the conflict target for the INSERT ... ON CONFLICT DO NOTHING (or equivalent) used by insertOrIgnoreReturning; an empty conflict target is invalid because the database cannot decide which rows to ignore without a key.

Source

Thrown at src/Illuminate/Database/Query/Builder.php:4213

            $this->cleanBindings(Arr::flatten($values, 1))
        );
    }

    /**
     * Insert new records into the database and returning specified columns with optional ignoring specific conflicts.
     *
     * @param  non-empty-array<non-empty-string>  $returning
     * @param  non-empty-string|non-empty-array<non-empty-string>|null  $uniqueBy
     * @return \Illuminate\Support\Collection
     */
    public function insertOrIgnoreReturning(array $values, array $returning = ['*'], array|string|null $uniqueBy = null)
    {
        if (empty($values)) {
            return new Collection;
        }

        if ($uniqueBy === [] || $uniqueBy === '') {
            throw new InvalidArgumentException('The unique columns must not be empty.');
        }

        if ($returning === []) {
            throw new InvalidArgumentException('The returning columns must not be empty.');
        }

        if (! is_array(array_first($values))) {
            $values = [$values];
        } else {
            foreach ($values as $key => $value) {
                ksort($value);

                $values[$key] = $value;
            }
        }

        $this->applyBeforeQueryCallbacks();

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Provide the conflict column(s): `insertOrIgnoreReturning($values, ['*'], 'email')` or `['tenant_id','email']`.
  2. Ensure the column(s) have a unique index/constraint in the schema, otherwise the conflict clause is meaningless.
  3. Validate before calling: `if (empty($uniqueBy)) throw new LogicException('uniqueBy required');`.
  4. If no conflict target exists, use plain `insert()` or `insertOrIgnore()` instead.

Example fix

// before
DB::table('users')->insertOrIgnoreReturning($rows, ['*'], []);
// => The unique columns must not be empty.

// after
DB::table('users')->insertOrIgnoreReturning($rows, ['*'], ['email']);
Defensive patterns

Strategy: validation

Validate before calling

$uniqueBy = array_filter(array_map('strval', (array) $uniqueBy));
if ($uniqueBy === [] ) {
    throw new \InvalidArgumentException('insertOrIgnoreReturning requires a non-empty $uniqueBy.');
}
$table->insertOrIgnoreReturning($values, ['*'], $uniqueBy);

Type guard

/** @param non-empty-string|non-empty-array<int,non-empty-string>|null $u */
function isValidUniqueBy(array|string|null $u): bool
{
    if (is_string($u)) { return $u !== ''; }
    if (is_array($u)) { return $u !== [] && array_all($u, fn($v) => is_string($v) && $v !== ''); }
    return false; // null is NOT valid here (target is mandatory)
}

Try / catch

// Validate $uniqueBy before the call; catching here is low value.

Prevention

When it happens

Trigger: Calling `insertOrIgnoreReturning($values, ['*'], [])` with an empty uniqueBy array. Passing `config('app.upsert_key', '')` when the config is unset. Building $uniqueBy from `array_keys($values)` after the values array was reduced to a single flat row.

Common situations: Generic repository helpers defaulting $uniqueBy to [] for flexibility; refactoring from upsert() (where uniqueBy is mandatory) to insertOrIgnoreReturning and forgetting to thread the key through; test code passing [] as a placeholder.

Related errors


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