firefly-iii/firefly-iii · error · FireflyException

400004

400004

Error message

400004: Could not store new currency.

What it means

TransactionCurrencyFactory::create() inserts name, code, symbol, decimal_places (enabled forced to false) inside a try/catch. A QueryException is logged and rethrown as '400004: Could not store new currency.' — the SQL error is in the preceding log line. Note the factory does not check for an existing code first, so duplicates land here rather than in a friendly path.

Source

Thrown at app/Factory/TransactionCurrencyFactory.php:70

            $old->forceDelete();
            Log::warning(sprintf('Force deleted old currency with ID #%d and code "%s".', $old->id, $data['code']));
        }

        try {
            /** @var TransactionCurrency $result */
            $result = TransactionCurrency::create([
                'name'           => $data['name'],
                'code'           => $data['code'],
                'symbol'         => $data['symbol'],
                'decimal_places' => $data['decimal_places'],
                'enabled'        => false,
            ]);
        } catch (QueryException $e) {
            $result = null;
            Log::error(sprintf('Could not create new currency: %s', $e->getMessage()));
            Log::error($e->getTraceAsString());

            throw new FireflyException('400004: Could not store new currency.', 0, $e);
        }

        return $result;
    }

    public function find(?int $currencyId, ?string $currencyCode): ?TransactionCurrency
    {
        $currencyCode = e($currencyCode);
        $currencyId   = (int) $currencyId;
        $currency     = null;

        if ('' === $currencyCode && 0 === $currencyId) {
            Log::debug('Cannot find anything on empty currency code and empty currency ID!');

            return null;
        }

        // first by ID:

View on GitHub (pinned to fd8791d08d)

Solutions

  1. Check the logged 'Could not create new currency:' line for the SQL error.
  2. Call find($currencyId, $currencyCode) before create() and only create when it returns null.
  3. Truncate name/symbol to their column limits and cast decimal_places to int (0–12).

Example fix

// before
$newCurrency = $factory->create(['name' => 'Euro', 'code' => 'EUR', 'symbol' => '€', 'decimal_places' => 2]);

// after
$currency = $factory->find(null, 'EUR') ?? $factory->create(['name' => 'Euro', 'code' => 'EUR', 'symbol' => '€', 'decimal_places' => 2]);
Defensive patterns

Strategy: validation

Validate before calling

$existing = $factory->find(null, $data['code']);
if (null !== $existing) {
    return $existing; // never insert a duplicate code
}
if (strlen((string) $data['code']) > 255 || !is_numeric($data['decimal_places'] ?? null)) {
    throw new InvalidArgumentException('Invalid currency payload');
}

Try / catch

try {
    $currency = $factory->create($data);
} catch (FireflyException $e) {
    $currency = $factory->find(null, $data['code']);
    if (null === $currency) {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Creating a currency whose code (or name/symbol) already exists and hits a unique index; code/symbol/name exceeding column lengths; decimal_places outside the column's allowed range; a non-numeric decimal_places.

Common situations: Re-running setup or import routines that call create() unconditionally instead of find(); ISO 4217 codes colliding with existing rows; long currency names from third-party feeds.

Related errors


AI-assisted analysis of firefly-iii/firefly-iii@fd8791d08d (2026-08-17). Data as JSON: /api/errors/fe053546f53ea953. Report an issue: GitHub.