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
- Check the logged 'Could not create new currency:' line for the SQL error.
- Call find($currencyId, $currencyCode) before create() and only create when it returns null.
- 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
- Always find-before-create on currency codes; they are unique.
- Bound name/symbol lengths and cast decimal_places to int.
- In importers, cache created currencies by code to avoid repeated inserts.
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
- 400000
- 400003
- 400005
- Query exception when creating transaction: %s
- Please use api/v1/currencies/default instead.
AI-assisted analysis of firefly-iii/firefly-iii@fd8791d08d (2026-08-17).
Data as JSON: /api/errors/fe053546f53ea953.
Report an issue: GitHub.