Is there a way, without modifying the core, to create a term for a custom taxonomy programmatically that is non deletable?

"Uncategorized" is an example, but in my case, it does not have to be the default term if a post is not attached a term, but yes it will be a default always existing term in that custom taxonomy.


By non-removable, mostly I mean on front-end. That is, the delete link and bulk action checkbox will not show, just like "Uncategorized".

4 Answers 4



Indeed, there is a way to define one term per taxonomy as default term which makes it non-deletable from the admin GUI.

WP_Terms_List_Table looks for an option get_option( 'default_' . $this->screen->taxonomy ).

So if you have a custom Taxonomy called genre, you have to set an option default_genre with the term-id of the term you want to protect from being deleted via the admin GUI:

$taxonomy = 'genre';
$key = get_option( 'default_' . $taxonomy );
$term = get_term_by( 'name', 'Your default genre term', $taxonomy );
if ( empty( $key ) && $term )
     update_option( $key, $term->term_id );

tl;dr In my first answer I missed this feature. But the description of the capability handling is still valid.

first answer

At this point, it is not possible. That is because in WP_Terms_List_Table all capability checks are made like this:

current_user_can( $tax->cap->delete_terms );

That means, all the view checks is, if the user has the privilege to delete terms by this category overall. It would be possible, if the function call looks like that:

current_user_can( $tax->cap->delete_terms, $tag )

where $tag would be the term object.

In this case you could easily filter map_meta_cap (where the capability check and the second parameter is passed through) to revoke the privilege to delete a specific term.

By the way, this is the default behaviour in WP_Posts_List_Table, so it's possible to check capabilities for each single post.

The only way I see at the moment is, to register a taxonomy without the default admin UI elements and build a custom admin page to manage the terms by your needs.

  • Glad to know this. By the way, is there something like a WP_Category_List_Table that exclusively makes "Uncategorized" a special case?
    – karbuncle
    Commented Aug 22, 2014 at 16:19
  • @karbuncle Your question pointed me to the solution. The way, WordPress handles the default category can also be used to define one term per taxonomy as a default term. See my updated answer on how you can do this.
    – David
    Commented Aug 24, 2014 at 10:10
  • Actually I found that out also and was trying to apply it first. Thank you all! I will accept your answer.
    – karbuncle
    Commented Aug 24, 2014 at 11:04

To just do it in a front-end way, you can remove the delete action using a filter, and (as there doesn't appear to be a filter to do this) disable & hide the checkbox using jquery:

define( 'PROTECTED_TERM_ID', 2 );
define( 'CUSTOM_TAX', 'genres' );
function wpse158784_custom_tax_row_actions( $actions, $tag ) {
    if ( $tag->term_id == PROTECTED_TERM_ID ) unset( $actions['delete'] );
    return $actions;
function wpse158784_admin_footer_edit_tags_php() {
    if ( ! isset( $_GET['taxonomy'] ) || $_GET['taxonomy'] != CUSTOM_TAX ) return;
    <script type="text/javascript">
    jQuery(document).ready(function() {
        jQuery('input#cb-select-<?php echo PROTECTED_TERM_ID; ?>').prop('disabled', true).hide();
if ( is_admin() ) {
    add_filter( CUSTOM_TAX . '_row_actions', 'wpse158784_custom_tax_row_actions', 10, 2 );
    add_action( 'admin_footer-edit-tags.php', 'wpse158784_admin_footer_edit_tags_php' );
  • Thank you and this is I believe the only way to go if somebody wants more than one "default" term.
    – karbuncle
    Commented Aug 25, 2014 at 7:45

I'm not sure if you can stop a term from being removed, but you can make sure it always exists. Just throw this in your functions.php (or plugin):

if ( !term_exists('locked-term', 'category' ) )
  wp_insert_term('Locked Term', 'category', array('slug' => 'locked-term'));

So, to be clear, technically they can remove the term, but the next time the site reloads, the term will be restored. This is effectively locking the term.

  • 2
    Not tested yet, but will all existing posts attached to this term lose the taxonomy attachment because the "real" term was deleted?
    – karbuncle
    Commented Aug 22, 2014 at 15:49
  • Yes, when the term is deleted, all assignments gets lost. The new term would be an »emtpy« one.
    – David
    Commented Aug 22, 2014 at 16:08
  • That's true. I found this works fine as far as ensuring a slug to be a certain way for a term. You could also go the old fashion route with this and add a "Do Note Delete" in the term description. :)
    – Jason
    Commented Aug 22, 2014 at 19:51
  • 1
    You can see here how the uncategorized term is added. Later, all posts are simply applied the term in various contexts. So, really, it doesn't keep it's literal assignments either.
    – Jason
    Commented Aug 22, 2014 at 19:57

Late to the party - you can filter the meta cap map and set the cap to 'do_not_allow':

add_filter( 'map_meta_cap', 'my_map_meta_cap, 10, 4 );
function my_map_meta_cap( $caps, $cap, $user_id, $args ) {
    $some_condition = get_my_specific_condition($caps, $cap, $user_id, $args);

    if ( 'delete_term' === $cap && $some_condition ) {
        $term_id = $args[0];

        $undeletable_ids = array(
            get_an_id($caps, $cap, $user_id, $args, $term_id),

        if ( in_array( $term_id, $undeletable_ids, true ) ) {
            $caps = array( 'do_not_allow' );

    return $caps;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.