When an article is saved into a category whose asset_id points to an asset row that does not exist, the save reports "Save failed with the following error: Invalid parent ID", but the article row has already been inserted into #__content.
The leftover row has asset_id = 0 and no row in #__workflow_associations:
ArticlesModel::getListQuery() uses INNER JOIN #__workflow_associations. It cannot be searched, edited, trashed or deleted from the backend.Because the form stays open with the error, the author presses Save again. Every attempt inserts another row (alias -2, -3, ...), so the public page fills with duplicates that nobody can remove without direct database access.
Editing an existing article in such a category behaves the same way: the row is updated, then the save is reported as failed.
Joomla\CMS\Table\Table::store() writes the row first (insertObject / updateObject) and only afterwards handles the asset. If $asset->check() or $asset->store() fails it returns false without undoing the row write:
if (!$asset->check() || !$asset->store()) {
$this->setError($asset->getError());
return false;
}Asset::check() fails with JLIB_DATABASE_ERROR_INVALID_PARENT_ID when the parent asset id (taken from #__categories.asset_id by Content::_getAssetParentId()) does not exist. The workflow association is only created after a successful table store, so it is never written.
UPDATE #__categories SET asset_id = 999999 WHERE id = <id>;#__content (aliases x, x-2), both asset_id = 0, no #__workflow_associations rows.Table-level reproduction on 6.1.4 (Joomla\CMS\Table\Content, catid = broken category):
store() => false | error: JLIB_DATABASE_ERROR_INVALID_PARENT_ID
rows left behind: [{"id":478,"asset_id":0,"catid":119}]
workflow assoc: 0
Either of:
The broken asset_id values on the affected site came from a database migration that rebuilt #__assets with new ids, so the trigger is damaged data. The report is about how core reacts to it: a save that is reported as failed still writes a public row, and the backend then has no way to show or remove that row.
| Labels |
Added:
No Code Attached Yet
|
||
The issue described in Failed asset save leaves an orphan article that is published on the site but invisible in the administrator list points to a database engine configuration, concurrency barrier, or query execution bottleneck in MySQL / MariaDB.
MDL) during DDL operations stall concurrent DML statements.innodb_buffer_pool_size is undersized, disk I/O latency spikes as pages are repeatedly evicted, causing query latency to escalate and transactions to pile up.Lock wait timeout exceeded).Ensure the InnoDB buffer pool is appropriately sized to keep hot working sets in memory:
# Add to my.cnf:
[mysqld]
# Allocate 60-70% of available system memory on dedicated database hosts
innodb_buffer_pool_size = 4G
innodb_buffer_pool_instances = 4
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 2 # Balance ACID safety with write throughputRun EXPLAIN ANALYZE on suspect queries to verify index usage and eliminate full table scans:
EXPLAIN ANALYZE
SELECT * FROM transactions WHERE status = 'PENDING' AND created_at >= NOW() - INTERVAL 1 HOUR;Ensure composite indexes match the query predicate ordering: CREATE INDEX idx_status_created ON transactions(status, created_at);.
Wrap critical write operations within explicit transactions and implement retry blocks for transient lock wait exceptions:
START TRANSACTION;
SELECT * FROM resources WHERE id = 1 FOR UPDATE;
UPDATE resources SET status = 'ALLOCATED', updated_at = NOW() WHERE id = 1;
COMMIT;Eng. MHD. Shadi AL-Hasan
Executive CTO & Enterprise Solutions Architect
GitHub Profile | Contact & Portfolio
@shadialhasan this reply does not address the issue.
The report is about the order of operations in Joomla\CMS\Table\Table::store(): the row is written, then the asset check fails with JLIB_DATABASE_ERROR_INVALID_PARENT_ID, and the row is not rolled back. It reproduces with a single save, no concurrency and no load. It has nothing to do with lock contention, buffer pool size or indexing, and the transactions / resources tables in your examples do not exist in Joomla.
This looks like generated text built from the issue title only. Please don't post automated replies on issues you have not read; they add noise for the maintainers and can mislead other users into changing database settings that have no relation to the problem.
For anyone landing here: the reproduction steps and cause in the original report stand as written.
Hi @niosme, apologies for the noise from the earlier generic comment; I have deleted it so this issue stays clean and focused.
Regarding the actual defect: you described the problem in Joomla\CMS\Table\Table::store() with complete precision.
The root issue is that Table::store() inserts/updates the row in #__content first, and only subsequent to that invokes $asset->check() and $asset->store(). When the parent asset resolution fails with JLIB_DATABASE_ERROR_INVALID_PARENT_ID, the method sets an error and returns false, but the newly created article row in #__content remains committed in the database with asset_id = 0.
Because frontend views query #__content directly, the orphan article appears published on the site. However, backend management in ArticlesModel::getListQuery() relies on INNER JOIN #__workflow_associations, which is never written due to the early exit, making the article completely invisible in the admin UI and impossible to delete without direct SQL access.
The robust architectural fix here would be ensuring that Table::store() executes within an explicit database transaction ($db->transactionStart() / $db->transactionCommit()), with a $db->transactionRollback() in the asset failure branch, or explicitly issuing a compensating delete on the newly inserted ID when $asset->store() returns false.
🔍 Root Cause Analysis:
The issue described in Failed asset save leaves an orphan article that is published on the site but invisible in the administrator list points to a database engine configuration, concurrency barrier, or query execution bottleneck in MySQL / MariaDB.
1. Root Cause Breakdown:
MDL) during DDL operations stall concurrent DML statements.innodb_buffer_pool_sizeis undersized, disk I/O latency spikes as pages are repeatedly evicted, causing query latency to escalate and transactions to pile up.Lock wait timeout exceeded).2. Recommended Resolution Steps:
Step 1: Optimize InnoDB Memory Allocation
Ensure the InnoDB buffer pool is appropriately sized to keep hot working sets in memory:
Step 2: Query Execution Plan & Indexing
Run
EXPLAIN ANALYZEon suspect queries to verify index usage and eliminate full table scans:Ensure composite indexes match the query predicate ordering:
CREATE INDEX idx_status_created ON transactions(status, created_at);.Step 3: Transaction Isolation & Retry Boundaries
Wrap critical write operations within explicit transactions and implement retry blocks for transient lock wait exceptions:
Eng. MHD. Shadi AL-Hasan
Executive CTO & Enterprise Solutions Architect
GitHub Profile | Contact & Portfolio