odoo/odoo · error · UserError
%(move)s reached an invalid state regarding its related stat
Error message
%(move)s reached an invalid state regarding its related statement line.\nTo be consistent, the journal entry must always have exactly one suspense line.
What it means
Same synchronization method, next invariant: after classifying the move's lines, at most one line may hit the journal's suspense account (len(suspense_lines) > 1 raises). The suspense account is a temporary balancing account; multiple suspense lines on one statement-line move are an invalid intermediate state.
Source
Thrown at addons/account/models/account_bank_statement_line.py:751
st_line_vals_to_write.update({
'payment_ref': liquidity_lines.name,
'partner_id': liquidity_lines.partner_id.id,
})
# Update 'amount' according to the liquidity line.
if journal_currency:
st_line_vals_to_write.update({
'amount': liquidity_lines.amount_currency,
})
else:
st_line_vals_to_write.update({
'amount': liquidity_lines.balance,
})
if len(suspense_lines) > 1:
raise UserError(_(
"%(move)s reached an invalid state regarding its related statement line.\n"
"To be consistent, the journal entry must always have exactly one suspense line.",
move=st_line.move_id.display_name,
))
elif len(suspense_lines) == 1:
if journal_currency and suspense_lines.currency_id == journal_currency:
# The suspense line is expressed in the journal's currency meaning the foreign currency
# set on the statement line is no longer needed.
st_line_vals_to_write.update({
'amount_currency': 0.0,
'foreign_currency_id': False,
})
elif not journal_currency and suspense_lines.currency_id == company_currency:
# Don't set a specific foreign currency on the statement line.View on GitHub (pinned to 1e661df964)
Solutions
- Reduce to a single suspense line: reconcile/replace the extra suspense lines with real counterpart accounts.
- Edit the statement line itself (amount, partner) instead of hand-editing the generated move lines.
- Cancel and re-create the statement line to regenerate a clean move if the entry is heavily modified.
Example fix
# before
env['account.move.line'].create({
'move_id': st_line.move_id.id,
'account_id': journal.suspense_account_id.id, ...}) # 2nd suspense line
# -> UserError
# after (single counterpart: edit the statement line / reconcile the suspense line)
st_line.write({'amount': corrected_amount}) Defensive patterns
Strategy: validation
Validate before calling
def move_has_at_most_one_suspense_line(st_line):
liquidity, suspense, other = st_line._seek_for_lines()
return len(suspense) <= 1 Try / catch
from odoo.exceptions import UserError
try:
move.write({'line_ids': line_commands})
except UserError as e:
if 'exactly one suspense line' in str(e):
raise # remove/replace extra suspense lines, then retry
raise Prevention
- Keep at most one suspense-account line per statement-line move; reconcile extras to real accounts.
- Avoid adding manual lines on journal.suspense_account_id.
- Recreate the statement line when the generated move needs structural changes.
When it happens
Trigger: Editing the move lines of a statement line so that two or more lines use the journal's suspense_account_id — e.g. manually adding a second suspense-account line or duplicating the counterpart line before reconciliation.
Common situations: Manual journal-entry edits that add lines on the suspense account; custom code writing suspense lines; partial reconciliation tooling leaving duplicate suspense counterparts.
Related errors
- You can't create a new statement line without a suspense acc
- The journal entry %s reached an invalid state regarding its
- A statement should only contain lines from the same journal.
- Unable to create a statement due to missing transactions. Yo
- The foreign currency must be different than the journal one:
AI-assisted analysis of odoo/odoo@1e661df964 (2026-08-15).
Data as JSON: /api/errors/cc51ff1a8c5535be.
Report an issue: GitHub.