Skip to content

Matrix field with Entry Types (Craft 5): imported blocks are created but never linked in elements_owners → invisible in CP/frontend #1733

Description

@ballmannweber

Environment
Craft CMS: 5.9.x
Feed Me: 6.11.0 (latest as of 2025-10-31)
PHP: 8.x
DB: MySQL 8 / MariaDB 10.x
Feed source: Element API JSON (Craft 4 on the producer side)
Target field: craft\fields\Matrix (the new Craft-5 Matrix that uses Entry Types as block types, not legacy MatrixBlock).

Summary
When Feed Me imports into a Craft-5 Matrix field that uses Entry Types (craft\fields\Matrix with entryTypes: in the field config), the nested block entries are created in the entries / elements / elements_sites tables, but the corresponding rows in elements_owners (elementId, ownerId, sortOrder) are not written.

As a result:

The import log says Entry updated successfully.
The block entries exist in the DB (correct primaryOwnerId, correct fieldId, correct typeId, correct content in elements_sites.content).
But because elements_owners has no matching row, Craft does not surface these blocks anywhere — the Matrix field appears empty in the CP and in Twig.
Every additional import run creates a new set of orphan block entries; nothing is cleaned up, so block counts accumulate (e.g. 9 source blocks → 45 orphan blocks in DB after 5 runs).

Reproduction
Create a Matrix field theContentBuilder of type craft\fields\Matrix with several Entry Types as block types (e.g. theText, theHeading, theImage).

Attach it to a channel section (e.g. exhibitions).

Configure a Feed Me feed with JSON source where each item has an array node like:

"theContentBuilder": [
{ "type": "textBlock", "targetEntryType": "theText", "textContent": "

" },
{ "type": "headingBlock", "targetEntryType": "theHeading", "headline": "Foo" }
]
In Feed Me mapping:

Block Type node: Set with field mapping → source targetEntryType
Sub-fields mapped to each Entry Type’s fields.
Run the import. Log reports success.

Expected
Block entries exist and are linked via elements_owners, so the Matrix field shows the imported blocks in the CP/frontend.
Re-running the same feed updates existing blocks (or replaces them) instead of accumulating new orphans.

Actual
Run this SQL after an import to verify:

SELECT
  COUNT(*) AS total_block_entries,
  SUM(CASE WHEN eo.elementId IS NULL THEN 1 ELSE 0 END) AS orphan_blocks
FROM entries e
JOIN fields f ON f.id = e.fieldId
LEFT JOIN elements_owners eo
  ON eo.elementId = e.id AND eo.ownerId = e.primaryOwnerId
WHERE f.handle = 'theContentBuilder';

On our system (after several import runs):

total_block_entries: 2862
orphan_blocks: 1422 (49.7%) — including 100% of the blocks for some parent entries.
For a concrete owner (entry ID 1539748, “Arcipelago”): 46 block entries existed in entries with correct primaryOwnerId = 1539748 and fieldId = theContentBuilder.id, but zero rows in elements_owners with ownerId = 1539748 → the Matrix field showed up empty in the CP.

After manually inserting the missing elements_owners rows (with sortOrder), the blocks immediately become visible in the CP without any further code change — confirming that the only missing piece is the elements_owners write.

Repair workaround we currently use
Because the imports keep producing orphans, we run a post-import SQL script that:

Keeps only the youngest import cluster per parent entry (60s tolerance).
Deletes older / orphan block entries (cascades through elements).
Inserts the missing elements_owners rows with sortOrder derived from ROW_NUMBER() OVER (PARTITION BY ownerId ORDER BY blockId).
After running this against ~100 exhibitions × 3 sites:

1625 orphan/duplicate blocks deleted
719 missing elements_owners rows inserted
252 parent entries now correctly show their Matrix content
orphans_left = 0
Full script available on request.

Suspected location in the code
vendor/craftcms/feed-me/src/fields/Matrix.php::parseField() produces the right new{handle}{i}.type / .enabled / .fields.* structure, but somewhere in the save pipeline for the new Craft-5 Matrix (Entry-Type based) the owner relation is not persisted. The legacy MatrixBlock-based path works correctly on the same feed.

Additional notes
Source data is fine and verified (JSON is well-formed, every block has type + targetEntryType + payload).
Block Type mapping in Feed Me is set to Set with field mapping → targetEntryType, with valid Entry-Type handles.
The bug is fully reproducible on a clean test entry.
Happy to provide a sanitized feed JSON + project-config dump on request.
:-)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions