Summary
The WP-CLI bridge auto-decodes any argument value that parses as JSON before writing, in includes/class-wpvibe-cli.php:
post meta update handler (~line 2064):
$value = $positional[2];
// Auto-decode JSON values.
$decoded = json_decode( $value, true );
if ( null !== $decoded ) {
$value = $decoded;
}
update_post_meta( $post_id, $key, $value );
- Same pattern in
option update (~1575) and option add (~1621).
This makes it impossible to store a literal JSON string, and silently changes the stored type: a JSON string argument becomes a PHP array, which WordPress then serializes.
Real-world breakage
SeedProd stores theme-template conditions as a JSON string in _seedprod_theme_template_condition and calls json_decode() on it when rendering the Theme Builder admin table. Writing that meta through the bridge:
post meta update 41 _seedprod_theme_template_condition '[{"condition":"include","type":"is_page(x)","value":"40"}]'
stored a:1:{i:0;a:3:{...}} (serialized array) instead of the JSON string, producing a WP critical error (admin screen fatal):
PHP Fatal error: Uncaught TypeError: json_decode(): Argument #1 ($json) must be of type string, array given
in .../seedprod-coming-soon-pro-5/admin/includes/class-seedprod-theme-templates-table.php:384
Any plugin that stores JSON strings in meta/options (a very common pattern) is affected the same way.
Divergence from real wp-cli
Real wp-cli stores values as plaintext by default and only decodes with an explicit --format=json. The bridge guesses based on content, so callers can't control the stored type — and the same command produces different stored data in real wp-cli vs the bridge.
Suggested fix
Match wp-cli semantics: default to plaintext, honor --format=json (the flag parser already exists) for explicit decoding. If backwards compat with current behavior is a concern, at minimum honor --format=plaintext as an opt-out.
Found while building a site through WPVibe MCP (plugin v1.4.0, WP 7.0). Companion defensive-fix issue filed on the SeedProd side.
Summary
The WP-CLI bridge auto-decodes any argument value that parses as JSON before writing, in
includes/class-wpvibe-cli.php:post meta updatehandler (~line 2064):option update(~1575) andoption add(~1621).This makes it impossible to store a literal JSON string, and silently changes the stored type: a JSON string argument becomes a PHP array, which WordPress then serializes.
Real-world breakage
SeedProd stores theme-template conditions as a JSON string in
_seedprod_theme_template_conditionand callsjson_decode()on it when rendering the Theme Builder admin table. Writing that meta through the bridge:stored
a:1:{i:0;a:3:{...}}(serialized array) instead of the JSON string, producing a WP critical error (admin screen fatal):Any plugin that stores JSON strings in meta/options (a very common pattern) is affected the same way.
Divergence from real wp-cli
Real wp-cli stores values as plaintext by default and only decodes with an explicit
--format=json. The bridge guesses based on content, so callers can't control the stored type — and the same command produces different stored data in real wp-cli vs the bridge.Suggested fix
Match wp-cli semantics: default to plaintext, honor
--format=json(the flag parser already exists) for explicit decoding. If backwards compat with current behavior is a concern, at minimum honor--format=plaintextas an opt-out.Found while building a site through WPVibe MCP (plugin v1.4.0, WP 7.0). Companion defensive-fix issue filed on the SeedProd side.