Skip to content

With a persistent object cache, "wp option set-autoload <name> off" updates the DB row but writes the option back into the cached "alloptions". #687

Open

Description

@orenwolf

Bug Report

Describe the current, buggy behavior

With a persistent object cache, wp option set-autoload <name> off updates the DB row but writes the option back into the cached alloptions. The option keeps being loaded on every request until something invalidates alloptions (with our object cache that key has no TTL, so in practice until a manual flush).

Option_Command::set_autoload() reuses update_option()'s "refresh the value where it is currently cached" logic:

$alloptions = wp_load_alloptions( true );
if ( isset( $alloptions[ $option ] ) ) {
        $alloptions[ $option ] = $previous->option_value;
        wp_cache_set( 'alloptions', $alloptions, 'options' );
} else {
        wp_cache_set( $option, $previous->option_value, 'options' );
}

That is right for a value change, but for an autoload change the option should leave alloptions (off) or alloptions should be invalidated (on).

Knock-on effect in core (7.1.4, from reading wp-includes/option.php): the DB now says off while the option is still in the cached alloptions. A later delete_option() sees a non-autoloaded row and only runs wp_cache_delete( $option, 'options' ), leaving the stale alloptions entry. get_option() checks alloptions before notoptions, so the deleted option keeps returning its old value, and a following update_option() treats that as the old value, runs an UPDATE matching 0 rows and returns false, so the option is never re-created.

We hit this on a production site: after set-autoload … off for 9 options, the cached alloptions hash had 517 entries = 508 autoloaded rows in the DB + those 9. wp cache delete alloptions options fixed it.

Describe how other contributors can replicate this bug

With any persistent object cache active:

wp option add autoload_test foo --autoload=on
wp option set-autoload autoload_test off
wp option get-autoload autoload_test
# off
wp eval 'var_dump( array_key_exists( "autoload_test", wp_load_alloptions() ) );'
# bool(true)

Describe what you would expect as the correct outcome

bool(false): after switching to off, the option is no longer in the cached alloptions (cached individually instead), and switching to on makes it part of alloptions on the next load.

Let us know what environment you are running this on

WP-CLI version: 2.12.0, WordPress 7.1.4, Object Cache Pro 1.26.1 on Valkey 8.2. (The same set_autoload() code is on entity-command main / v3.0.5.)

Provide a possible solution

On WP ≥ 6.4, delegate to core wp_set_option_autoload( $option, $autoload ), which updates the DB and the alloptions cache via wp_set_option_autoload_values(). As a fallback for older WP: when switching to a non-autoloaded value, unset() the entry from alloptions and wp_cache_set( $option, … ); when switching to an autoloaded value, wp_cache_delete( 'alloptions', 'options' ).

Workaround: run wp cache delete alloptions options after wp option set-autoload.

Provide additional context/Screenshots

Related but separate WP 6.6 autoload issue: #670 (wp option list --autoload misses auto/auto-on/auto-off).

Thanks!

Activity

  1. nathanrice commented on Nov 4, 2026

    @nathanrice

    How concerned should we be about making sure we include workarounds (that will likely exist in the code forever) for EOL versions of WordPress? 6.3.x is very old, and this bug doesn't seem critical, so I would support leaving out any workarounds for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions