I'm not sure if this is intended or not, but it looks to me like when a layout builder created block (aka non reusuable) is removed from a layout, it removes visually from the layout on the edit or view mode, but the block itself remains in the database, as a non-reusable block. This essentially makes an orphaned block, as it is no longer accessible from the UI. This poses a scalability problem, when over time, a site has the potential of have numerous useless rows in the database.
My proposed solution would be to have a nonreusable block removed from the `block_content` table (and related field tables) when removed from its layout, or at the very least providing a way for a user to remove these blocks from the database.
I'm looking for feedback on this idea before putting time into a solution on it. Any side-effects or challenges that could be raised against this?
| Comment | File | Size | Author |
|---|---|---|---|
| #66 | 3042418-66-reroll-d11.x.patch | 4.09 KB | apotek |
| #65 | 3042418-65-reroll-d10.4.5.patch | 3.36 KB | drdam |
| #64 | 3042418-38-reroll.patch | 3.65 KB | dragos-dumi |
| #50 | 3042418-50.patch | 17.97 KB | seanb |
| #50 | interdiff-48-50.txt | 3.04 KB | seanb |
Issue fork drupal-3042418
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
tim.plunkettThe blocks are cleaned up when cron is run.
Comment #3
capysara commentedAt this time, if the blocks are removed from the entity via the layout, but the entity itself is not deleted, those blocks will persist. Inline (non-reusable) blocks are only deleted when the actual node is deleted.
This behavior is by design. For example... If a node has revisions, then some of those revisions will be using the deleted block. The block-delete is not triggered (via cron) when you delete revisions (only when you delete the node). If you had 1,000s of revisions for 1,000s of nodes, you would have to check all the other revisions to see if the block was still there in order to delete it from the database. And, if you deleted your block from all the revisions in all your entities, your revisions would no longer really be revertable because you could end up with a bunch of broken/missing blocks.
I understand why it works this way, but I'm opening this ticket back up because I think it's relevant as a feature request. It would be nice to have a manual way to delete inline blocks, other than deleting them directly from the database. I'm not sure what the solution is. Maybe a use-at-your-own-risk warning that you could create broken blocks if you try to revert to a revision at some point in the future? Or maybe this feature should be a separate module altogether?
At a minimum, it would be helpful to have some documentation. "Removing custom inline blocks does not delete them from system." And then some concise explanation of why and the risks of just deleting manually from the database.
Comment #4
capysara commentedIf you delete a block from the Custom block library, you get a warning that it will “remove [number of] placed block instances.”This only happens if you have placed the block on the block layout page (/admin/structure/block).When you delete a block from the Custom block library, you get a “This block is broken/missing” in place of the block in both the current and previous revisions. Is it possible to use the same behavior for the non-reusables?
Comment #5
capysara commentedAs far as documentation, I would propose the following changes (and can create a patch).
Add language when you are in layout and you go to remove an inline, custom block:
Add language when you are in the custom block library and you delete a custom block:
Add a new header ("Other") and items to /admin/help/layout_builder:
Other
Removing vs deleting blocks
Comment #6
hawkeye.twolfI'd like to be able to delete a custom block bundle without deleting all the nodes (or other LB-enabled entity types) that once had an inline block of that type on it. I.e., if I've cleared all node revisions that referenced a given inline block, I'd like the reference removed from the
inline_block_usagetable.re:
Indeed hard with Layout Builder data serialized into a field table! But if the
inline_block_usagetable tracked layout entity revision in addition to layout entity ID, we'd be in business. As @capyara pointed out (Hi, btw!), this would require tracking entity revision deletion in addition to overall deletion of the entity.tl;dr: Anybody have a feel for LOE (and potential side effects) of adding host entity revision IDs to the
inline_block_usagetable?Comment #7
geek-merlinA strong +1 to add revision AND language fields. See my elaboration in related entity_usage issue.
Imho that change will not have a big impact code-wise. Of course the table will grow a lot bigger. But we had the same thing with paragraphs. People started with ordinary reference fields. Then someone wanted revisions. And in the end people saw that we need EntityRevision(+Translation)Reference field. Of course this makes an awful lot of revisions, but we have to eat that frog to get correct results.
Comment #8
geek-merlin@capysara #5: If you create a patch, i can help reviewing it.
Comment #9
johnwebdev commentedComment #11
finex commentedHi, same problem here: I've created a custom block type, I've added on some test content and after I've deleted the nodes. Now I cannot remove the custom block type because "the block type is used from 1 customized block".
Comment #12
phjouI've got the same problem.
I wanted to remove an option from a text list field. But I can't because an inline block that has been removed from layout builder was using that value. When looking to the database I just noticed that the values were still there even after removing the block. Now I understand why thanks to this issue. But after removing the block the interface is broken when trying to edit the block.
So I just updated the values to something else directly in the database to unlock myself.
Comment #14
alexmoreno commented+1 same problem. Added custom block, then used in the layout builder. When removed from there the block stays and can't be removed with:
"is used by 1 custom block on your site. You can not remove this block type until you have removed all of the Images"
Comment #15
shubhangi1995Indeed this needs a fix , till maybe we can use drush to delete the block type itself
Ex: drush cdel block_content.type.test_block
Comment #16
bradallenfisher commentedany movement on this? We just ran into it as well. Cron run does not fix the issue.
Comment #17
sadman commentedI was only able to delete an inline, custom (non-reusable) block type after completely deleting the node the block was originally placed/created using Layout Builder. I'd mistakenly thought deleting all the revisions except the current revision and running cron would allow me to delete the block type. This was not true. Only after deleting the node and running cron again was I able to delete the block type.
Comment #18
tim.plunkettComments #3-9 are a feature request. #11-17 (and I think the original post) are a bug report. These are separate things.
For now I'll recategorize as a bug report, but this needs clear steps to reproduce, starting with "Installing Drupal's Standard profile, without contrib modules"...
Comment #19
capysara commentedSteps to reproduce:
On simplytest.me
* Go to your node /node/1
* Delete
* Go to Block types /admin/structure/block/block-content/types
* Try to Delete Basic block
* You still get the error message
* Go to cron config /admin/config/system/cron
* Run cron
* Go to Block types /admin/structure/block/block-content/types
* Try to Delete Basic block
* Success!
Comment #20
tim.plunkett#19 seems to me to describe a working scenario.
Where is the bug?
Comment #21
capysara commentedI only created the one block, and then I deleted it in Step 16. At this point, I have "removed all of the Basic block custom blocks." But the error message says that it still exists so it won't let me delete the Block type. The only way around that, that I've found, is to delete the entire node (the node that no longer has any blocks) and then run cron.
A use case where this might be problematic is if I create a new block type and add one to my homepage node that already has a bunch of content. Next week we decide not to use that block type anymore. Even after I delete the only block of that type, I can't delete the block type.
Comment #22
doostinharrell commented@capysara if you have revisions enabled you may have to delete any revisions that reference the block then run cron. I had to do this just now and it solved my issue (thx for the cron tip). Tracking down entities that reference these blocks can be tricky for non-developers as it requires writing sql queries.
It would be nice if we exposed the entity id, and revision id, that reference the block bundle to assist in this sort of clean-up.
Comment #23
capysara commentedMy understanding of the issue is that this^ is the root of the problem as originally described: when you remove LB created blocks, "the block itself remains in the database, as a non-reusable block" so you have to write an sql query to get rid of it. If I have deleted all my blocks of a block type, I would expect to be able to delete the block type (OP noted the potential scalability issues here). I understand that I can't delete it because of revisioning and that there are workarounds for it, but it still seems like a bug (to me). I wouldn't expect that I have to manually delete content (revisions created by default, that I may not know are there) from the database after I've deleted all the content that I'm aware that I created.
If the above is not considered a bug then, at a minimum, I don't think the error message is correct. It's definitely not clear, and it doesn't give any clues about being required to delete revisions from the database. "Basic block is used by 1 custom block on your site. You may not remove Basic block until you have removed all of the Basic block custom blocks." At this point, I think most people would assume that they had removed all the custom blocks. You can get this error message even if you don't have a single custom block on your site, so it's not possible to remove any more blocks--except by removing them directly from the database. All this behavior is by default; you don't have to explicitly enable revisions, or even be aware that revisions exist, to get blocked by this error.
Comment #24
tim.plunkettThe steps in #19 include the rather drastic step of "delete the whole node", and then everything after made sense.
Without that step, you're stuck. Which is your whole point!
This will help confirm that #22 is correct, and that we have to come up with something to address revisions...
Comment #25
capysara commented1. Yes, the content type has revisions enabled by default
2. I'm still hitting the error when I disable revisions:
On simplytest.me
Comment #26
sadman commentedMy testing was not as precise as @capysara but my results were the same. On my dev environment, it didn't matter if I had revisions enabled or not. It didn't matter if I deleted all revisions for a given node. If I'd ever added an inline block with layout builder on a given node, the only way I could delete the block type was to delete any and all nodes that block might have been used. Just giving a +1 to what is described in #19 and #25. Personally, I'd be OK with a GUI means to allow me to delete the block type in spite of it possibly existing in a revision. (To be clear, even if the conditions are satisfied that it is no longer in use, I can't delete the block.) Practically speaking, I'm not even sure I'd want to have to delete all the revisions of all the nodes a block might have been used. I think some are asking for an 'USE THIS AT YOUR OWN RISK' GUI button to delete a block that could break revisions and/or a current version of content. Leaving the burden on site administrators to ensure published content is not using the deleted block.
Comment #27
mr_scumbagI have the same problem.
Created a custom block type.
Use LB to create a single instance of the custom block type
Decided I didn't need it - "removed" (because LB doesn't say 'delete') this custom inline (LB created) block.
I cannot delete the custom block type as Drupal claims I still have 1 instance of the custom block type - the one I "removed" in LB above.
I cloned the system view "Custom block library" and changed a filter from "Custom Block: Reusable (= True)" to "Custom Block: Reusable(= False)" which now lists only inline blocks (ie LB created blocks).
Now normally this view allows "edit/clone/delete/devel" options from a dropdown - but my block only shows "devel". There is no option to delete - and looking in the DB I see this block has entries in revisions (as I can now see the block_id from the view)
This also screws up config:import/export - as trying to import config (to remove the custom block type - as I never exported it) also says "Entities exist of type Custom block and Custom block type BioKDF Image Block. These entities need to be deleted before importing." (Where 'BioKDF Image block' is my custom block type name).
Comment #28
aaron.ferris commentedSubbed, although running Cron resolved this for me.
Comment #29
Alana Corrêa commentedI have the same problem.
I created a block type in a custom library and used that in content from Layout Builder UI.
But even after an excluded this block of my content, I was not able to remove that list of block types.
I ran the cron, but that not work.
After, I excluded the contents, but I still no be able to remove them.
Just after I run cron again that I was able to remove it.
Comment #31
smustgrave commentedCan also confirm I have the same issue. I deleted a field from a content type using layout builder and it remained in the table. Now if you view a page using layout builder I get a dozen errors like "Notice: Undefined index: provider in"
Comment #32
pierregermain commentedCreated a custom block, tested the block in layout builder, deleted the block in layout builder. Now I can't delete the custom block. I don't have revisions enabled. Tried cron, that didn't help.
Deleting the affected rows from the DB was the only way that i found to get rid of this error.
Comment #34
nsciaccaI experienced the same issue described above, although not trying to delete the block type, but rather just tracking data in a migration.
In my migration from D7 Panelizer to D9 Layout Builder, where I am mapping Fieldable Panels Pane data to inline blocks, I found that on every re-run of the migration where I updated existing entities (instead of rolling them back) it created new instances of the blocks and did not delete the old ones. I was explicitly setting the value of the layout_builder__layout via process plugins which ignored old values and set new values on each run. I did not have revisions enabled.
It would be nice if the entity update hook could include a check for each of the blocks to see if they are used in any revision of the entity, and then delete those that are not.
Comment #35
goz commentedThis issue comes from \Drupal\layout_builder\InlineBlockEntityOperations
In our case, the node entity is revisionable, so blocks will not be deleted.
Comment #37
goz commentedThe MR allow blocks to be deleted in case they are not part of a revision.
Comment #38
ranjith_kumar_k_u commentedComment #39
goz commentedSomething broken translation with MR 2111 (and so patch #38).
I investigate to have more details.
Why patch #38 ? Do we have to generate and attach a patch ? MR is not enough ?
Comment #41
mohanrajthangarasu commentedHad the same issue. Not able to able to delete the block type, even after removing the relevant custom block from the layout builder. Running cron didnt help. Content type had revision enabled so deleted the older revisions and removed the affected/respective rows in "block_content" table. Then able to get ride of the error and successfully delete the block type via UI.
Comment #43
robbdavis commentedThis is still an issue.
Comment #44
dalemoore commentedJust ran into this issue as well. Not deleting my page, so my unused block type will have to remain for now. Deleted the block from the page, deleted all the fields from the block type, ran cron, nothing will allow me to get rid of it. :\
Comment #46
seanbAttached patch implements the suggestion in #7. I think adding a revision ID and langcode is the only way to properly track if blocks are still used in older revisions. Did not work on the tests yet, but this should give a rough idea on what this could look like.
Comment #47
seanbAnother patch to fix an issue not tracking blocks for non-affected translations (they still have usage obviously).
Comment #48
seanbSorry, my latest assumption was wrong. It was the missing use statement causing the issue.
Comment #49
seanbJust found this can be a problem when saving new revisions. Usage is added for blocks from
layout_builder_entity_presave(). When saving a new revision for an entity the revision ID is set to NULL.So this is a nice one, we need to save the blocks first, so we can properly store the block revision IDs in the layout. To track the usage for the blocks though, the layout entity needs to be saved as well.
I think we might need to move the usage tracking to an after save hook to get this to work.
Comment #50
seanbThis should fix it.
Comment #51
chrisscrumping commentedTested #50 and getting an error trying to apply the patch.
I also can't delete a test block type after testing layout builder on a dev branch, which is blocking me from moving to another branch without layout builder as I can't import the config until all the blocks types are gone.
Comment #52
chrisscrumping commentedI was able to get passed this by moving the handlPresave inside of the $revision instanceof condition.
Finally once I had deleted all my test nodes and ran cron I was able to delete the block type (after applying #50).
As said previously in this thread feels like an issue that could do with a better work around at very least. At least some way of seeing what content is stopping the delete would be useful.
Comment #53
delacosta456 commentedhi
if it may help the workaround for me without patching (until stable patch) is
select * from key_value where name = "field.field.deleted" or name = "field.storage.deleted";drush crclick Run
hope it's help
Thanks
Comment #54
atowl commentedHello,
I wondered where we are with this issue? As I've tried both the MR patch and the patch in #50.
As i see it the MR works, if you don't have revisioning turned on. It gets triggered once a block is removed, but because there would be a revision of the page, it returns early, not removing the block, and then is not triggered again, to actually clean up any block that was placed, (once revisions have been deleted).
For the patch in #50, this works.. kinda. Because it makes a new primary key it is truncating the inline_block_usage table. While this doesn't destroy a page layout, you can re-populate this table by saving the page, writing back the usage to the table. BUT if you run cron inbetween that time, it sees that the layout_entity_type and layout_entity_id fields are null, and will start removing the blocks, which then DOES destroy page layouts, like all of them. Not ideal.
I am wondering if we should start back with the first MR, but hook it into the page save.
For psuedo code It would
* Iterate over the blocks associated to that node
* Skip if included in any revisions
* Else set layout_entity_type, layout_entity_id to null (layout_builder_cron() cleans this up later)
* Not reusable block? delete from block_content table also.
I am not sure however if there are more places to remove the block from if any.
Now we also would need to do a cron task, (or just on a dbupdate), to remove unused blocks that are are already in the block_content table.
i think this is as easy as
* Get id from block_content table
* If id is not in inline_block_usage table, delete.
I get the feeling that there should be some bit here that looks into the blob of the node__layout_builder__layout table (or another table) for usage. But unsure which, if any.
thanks
Comment #55
frondeau commentedHello,
I hope this will help, for Drupal
10.3.510.3.9.As I was removing a block from in the builder layout, I've realised that the
inline_block_usageregister was still here with the values of the node inside thelayout_entity_typeandlayout_entity_idcolumns.But the
removeByLayoutEntity()function should replace the values by null.And the
layout_builder_cron()didn't remove the orphan non-rusable related blocK.First of all, I think there is a mistake inside the function
removeByLayoutEntity():the last query Condition should find the
block_content_idand not thelayout_entity_id, because the only parameter we get is the block_content $entity, and not the parent node.When this issue is fixed, the
layout_builder_cron()remove the related non reusable block_content.Writing soon to propose the fix :-)
Comment #56
frondeau commentedComment #57
frondeau commentedComment #58
frondeau commentedComment #59
frondeau commentedComment #60
frondeau commentedComment #61
fabrondeau commentedComment #62
fabrondeau commentedHello,
I've just removed my proposition as I'm documenting the process on my own to understand it, and I was confused in the managed entities. On the case of inline_block_usage, I understood that the required entity is the parent node, not the block itself.
In my case, the parent entity had some revisions that were related to the block. That's the reason why it could not be deleted.
Comment #63
flyke commentedThe MR does not apply to D11.2.0, D11.2.1 or D11.2.2
Comment #64
dragos-dumi commentedjust in case someone was using #38 or the MR of it, attached patch 38 rerolled for 11.2.5
Comment #65
drdam commentedReroll patch for D 10.4.5
Comment #66
apotek commentedHere is a patch rolled against Drupal 11.x as of Jan 16, 2026.
Comment #68
vasikeI checked the latest patch from #66
And it seems it works ... this mean I also confirm the issue.
However I also tried with a content type with Layout Builder Asymmetric Translation and it seems the patch doesn't solve
I will check if there if something quick and for this place about this "translation issue".
But first I thought to mention it, maybe there is something similar for other people.
Update here: it seems it works for some blocks not "translated/cloned" ?!?!?!
Update 2: it's worst actually ... when create translation with this module ... the patch deletes from original of translations ....
About MR ... I can't rebase ... but I could create new branch and MR for the main ...
p.s. I'll put also on Needs Review ... as it could help to gain attention for people that needs the latest patch ... at least and maybe also some review from others.
Comment #69
vasikeIt seems there could be some cases ... at least with Layout Builder Asymmetric Translation + No revision creation:
- Revisions count === 1 ... then we need to check against The unchanged entity
"loadUnchanged"- Check against translation of unchanged entity
Something like
Even it's core code and it should not break anything ... not sure this should be included here ... for now.
Or to provide a hook/event something maybe Before AND After the code here.
So "edge cases" could be covered from other modules.
Comment #70
smustgrave commentedThink it would be good to clean up the summary. Add the steps to reproduce. Then could probably review. We know we will need test coverage matching the steps