firefly-iii/firefly-iii · error · FireflyException
Source: %s
Error message
Source: %s
What it means
As part of journal creation, TransactionJournalFactory builds {id, name, iban, number} from the payload's source_* fields and runs AccountValidator::validateSource(). A false result throws FireflyException 'Source: <validator reason>' — the appended reason (accountValidator->sourceError) states why the account cannot be the source of this transaction type.
Source
Thrown at app/Factory/TransactionJournalFactory.php:643
private function validateAccounts(NullArrayObject $data): void
{
Log::debug(sprintf('Now in %s', __METHOD__));
$transactionType = $data['type'] ?? 'invalid';
$this->accountValidator->setUser($this->user);
$this->accountValidator->setTransactionType($transactionType);
// validate source account.
$array = [
'id' => null !== $data['source_id'] ? (int) $data['source_id'] : null,
'name' => null !== $data['source_name'] ? (string) $data['source_name'] : null,
'iban' => null !== $data['source_iban'] ? (string) $data['source_iban'] : null,
'number' => null !== $data['source_number'] ? (string) $data['source_number'] : null,
];
$validSource = $this->accountValidator->validateSource($array);
// do something with result:
if (false === $validSource) {
throw new FireflyException(sprintf('Source: %s', $this->accountValidator->sourceError));
}
Log::debug('Source seems valid.');
// validate destination account
$array = [
'id' => null !== $data['destination_id'] ? (int) $data['destination_id'] : null,
'name' => null !== $data['destination_name'] ? (string) $data['destination_name'] : null,
'iban' => null !== $data['destination_iban'] ? (string) $data['destination_iban'] : null,
'number' => null !== $data['destination_number'] ? (string) $data['destination_number'] : null,
];
$validDestination = $this->accountValidator->validateDestination($array);
// do something with result:
if (false === $validDestination) {
throw new FireflyException(sprintf('Destination: %s', $this->accountValidator->destError));
}
}
}View on GitHub (pinned to fd8791d08d)
Solutions
- Use a source account you own of an allowed type (asset, loan or debt) — fetch IDs from the accounts endpoint of the same user.
- When supplying only a name, ensure it uniquely matches an existing allowed account or leave it empty so the validator can resolve it.
- Verify the account still exists and is not deleted before submitting.
Example fix
// before ['type' => 'withdrawal', 'source_id' => $revenueAccount->id, ...] // revenue account cannot be a withdrawal source // after ['type' => 'withdrawal', 'source_id' => $assetAccount->id, 'destination_name' => 'Grocery store', ...]
Defensive patterns
Strategy: validation
Validate before calling
$source = Account::where('user_id', $userId)->find($data['source_id'] ?? 0);
if (null === $source || !in_array($source->accountType->type, ['Asset account', 'Loan', 'Debt'], true)) {
throw new InvalidArgumentException('Source must be an owned asset/loan/debt account');
} Type guard
function isAllowedSource(?\FireflyIII\Models\Account $a): bool
{
return null !== $a
&& in_array($a->accountType->type, ['Asset account', 'Loan', 'Debt'], true);
} Try / catch
try {
$journal = $factory->create($data);
} catch (FireflyException $e) {
if (str_starts_with($e->getMessage(), 'Source: ')) {
// re-map source_id / source_name using the validator's reason, then re-submit
}
throw $e;
} Prevention
- Fetch source IDs from the authenticated user's accounts at request time, never hardcode them.
- Check source/destination direction before submitting — swapped sides are the top cause.
- Keep account type rules per transaction type in one shared validator in your client.
When it happens
Trigger: source_id pointing at a deleted account or an account of another user; using an account type that may not fund the flow (an expense/revenue account as a withdrawal source — the source of a withdrawal must be an asset/loan/debt account); a source name+iban+number combination that resolves to no allowed account and cannot be auto-created.
Common situations: Hardcoded account IDs that drift between environments; CSV/OFX imports with foreign IBANs; forgetting that for transfers both sides must be personal accounts.
Related errors
- Destination: %s
- Unexpectedly could not find transaction
- Cannot make a recurring transaction of type "%s"
- Could not create recurring transaction: %s
- Query exception when creating transaction: %s
AI-assisted analysis of firefly-iii/firefly-iii@fd8791d08d (2026-08-17).
Data as JSON: /api/errors/ae34c06feb45cb45.
Report an issue: GitHub.