Steps to reproduce
- Register a custom post type with
'trash' in its supports array (or use add_post_type_support() after registration).
- Verify the support is registered correctly:
wp eval "echo post_type_supports('mycpt', 'trash') ? 'yes' : 'no';"
# → yes
- Attempt to soft-delete a post of that CPT:
wp post delete 1234
# → Warning: Posts of type 'mycpt' do not support being sent to trash.
# Please use the --force flag to skip trash and delete them permanently.
Expected behaviour
The post is sent to trash (post_status set to trash, _wp_trash_meta_status + _wp_trash_meta_time written), exactly as if you'd clicked the Trash button in WP admin.
Actual behaviour
The command warns that the CPT doesn't support trash and refuses to proceed without --force. --force then hard-deletes, losing the native 30-day reversibility window.
Environment
- WP-CLI 2.12.0
- WordPress 6.x (reproduced on multiple recent versions)
- PHP 8.2
Workaround
Call wp_trash_post() directly via wp eval-file — this is the same WP core function the trash button calls, and it correctly honours post_type_supports(). End state is identical (including the trash-meta keys for native 30-day auto-purge):
echo '<?php = [1234, 5678]; foreach ( as ) { = wp_trash_post(); echo "$id: " . ( ? "trashed" : "FAILED") . "\n"; }' > /tmp/trash.php
wp eval-file /tmp/trash.php
Hypothesis (please verify)
Symptom suggests the wp post delete subcommand inspects the WP_Post_Type::$supports instance property directly rather than calling post_type_supports(). WordPress copies that property into the global $_wp_post_type_features array during register_post_type() and then unsets the instance property, so the inspection returns an unexpectedly-missing value at later request times — even though post_type_supports() (which reads from the global array) returns the correct true.
If that hypothesis holds, the fix would be to swap any isset($post_type_object->supports['trash'])-style check inside the delete command for post_type_supports($post_type, 'trash').
Why it matters
Custom post types that have correctly opted in to trash via supports should be deletable to trash via WP-CLI the same way they are via wp-admin. The current behaviour silently nudges operators toward --force (irrecoverable) or workarounds like the wp_trash_post() invocation above.
Happy to provide more environment detail if useful.
Steps to reproduce
'trash'in itssupportsarray (or useadd_post_type_support()after registration).Expected behaviour
The post is sent to trash (
post_statusset totrash,_wp_trash_meta_status+_wp_trash_meta_timewritten), exactly as if you'd clicked the Trash button in WP admin.Actual behaviour
The command warns that the CPT doesn't support trash and refuses to proceed without
--force.--forcethen hard-deletes, losing the native 30-day reversibility window.Environment
Workaround
Call
wp_trash_post()directly viawp eval-file— this is the same WP core function the trash button calls, and it correctly honourspost_type_supports(). End state is identical (including the trash-meta keys for native 30-day auto-purge):Hypothesis (please verify)
Symptom suggests the
wp post deletesubcommand inspects theWP_Post_Type::$supportsinstance property directly rather than callingpost_type_supports(). WordPress copies that property into the global$_wp_post_type_featuresarray duringregister_post_type()and then unsets the instance property, so the inspection returns an unexpectedly-missing value at later request times — even thoughpost_type_supports()(which reads from the global array) returns the correcttrue.If that hypothesis holds, the fix would be to swap any
isset($post_type_object->supports['trash'])-style check inside the delete command forpost_type_supports($post_type, 'trash').Why it matters
Custom post types that have correctly opted in to trash via
supportsshould be deletable to trash via WP-CLI the same way they are via wp-admin. The current behaviour silently nudges operators toward--force(irrecoverable) or workarounds like thewp_trash_post()invocation above.Happy to provide more environment detail if useful.