Skip to content

Customizer responsive sanitizer fatals when input is an array #4651

Closed

Description

@pirate-bot

Summary

When a responsive Customizer setting is submitted as an array, the sanitizer can terminate the request with a TypeError instead of returning a sanitized value. This interrupts the affected Customizer preview request.

Customer context

Product / area: Neve, Header Footer Grid responsive Customizer settings
Version: Neve 4.2.16
Environment: WordPress 7.1.3; PHP 8.3.31 and 8.4.26
Integration / third party: Not identified as relevant
Reported error / symptom: json_decode(): Argument #1 ($json) must be of type string, array given
Impact: The fatal interrupts the affected Customizer preview request; two sites were represented in telemetry.

Reproduction notes

Production telemetry captured this during a frontend request in the Customizer preview/sanitization path on Neve 4.2.16. The exact responsive setting and submitted payload are not available. The source-level failure can be exercised by calling sanitize_responsive_int_json() with an array on a PHP version that rejects non-string input to json_decode(); no local runtime reproduction was performed.

Diagnosis

Conclusion

Confirmed product defect: production telemetry records an array reaching sanitize_responsive_int_json(), and the Neve 4.2.16 implementation passes that value directly to json_decode(). The reported TypeError matches this source path.

Where this likely occurs

  • header-footer-grid/Traits/Core.php — Abstract_Component::sanitize_responsive_int_json() approx. lines 77–86 calls json_decode($input, true) before validating the input type.
  • header-footer-grid/Core/Builder/Abstract_Builder.php — responsive setting registration approx. lines 400–402 attaches the sanitizer callback.
  • tests/test-neve-sanitization.php — do_assertion_for_sanitize_responsive_int_json() lines 241–243 JSON-encodes its array fixtures before invoking the sanitizer.

Engineering notes

The crash is reported for Neve 4.2.16 on PHP 8.3 and 8.4. The stack identifies the Customizer setting sanitization path, and the telemetry gives direct evidence that the callback received an array. Existing tests cover array-shaped values only after JSON encoding, not the native array input reported here. No regression boundary was identified; the method has existed since 2019.

Test coverage status

tests/test-neve-sanitization.php::test_sanitize_responsive_int_json() covers serialized JSON input, including fixtures initially represented as arrays. Its helper encodes those fixtures before the sanitizer call, so relevant native-array input coverage is absent from the inspected test path. No test suite was run.

What to verify or explore next

  • Reproduce the reported Customizer callback path with a native array input on the affected PHP versions.
  • Identify which responsive setting and client payload produce the array in the Customizer request.
  • Confirm behavior for existing serialized-string inputs alongside the reported native-array case.

Unknowns / follow-up

The exact setting identifier, request payload, and triggering user interaction were not captured in the telemetry report.

Confidence

Confidence: 98/100

The Neve 4.2.16 Customizer crash is confirmed by production telemetry and source: the sanitizer passes the reported array directly to json_decode(). The three loader fingerprints all resolve to an unguarded require in Neve 4.1.4/4.2.2/4.2.6, while v4.2.14 contains a missing-file guard, so those older-version signals do not warrant a new issue when a fixed release is available.

Crash telemetry

Occurrences 20
Distinct sites 2
First seen 2026-10-07 20:18 UTC
Last seen 2026-10-08 15:09 UTC
Crash location product:header-footer-grid/Traits/Core.php:78
Request context frontend
Inside Themeisle SDK no
Product versions 4.2.16
WP versions 7.1.3
PHP versions 8.3.31, 8.4.26
SDK versions 3.3.66

Source: automated crash report — neve, fingerprint dd32a661da5a24e257d2836e290e0a41
Generated by bug-report-triage (ID: bug-report-triage_6ac883627ef247.03851758)

Activity

  1. added theissue type on Oct 9, 2026
  2. self-assigned this
    on Oct 9, 2026
  3. milicavs commented on Oct 17, 2026

    @milicavs

    🧪 QA steps and test links

    On Neve 4.2.16 the Customizer crashes with a fatal error (json_decode(): Argument #1 ($json) must be of type string, array given) when a responsive header setting, such as a row's height, reaches it as already-decoded values instead of the usual saved text. Normal use of free Neve never does this; Neve Pro conditional headers do. The links and steps use a Code Snippet that hands the Customizer the Header Main row height the same way, so the crash shows on free Neve.

    Link Build Bot check
    Reproduce the bug Neve 4.2.16 (released) ✅ Reproduced: Appearance → Customize loads as an empty white page, and so does the Customizer preview
    Verify the fix PR #4662 build 9266937 ✅ Fixed: the Customizer and its preview load, and the Header Main row height setting holds 60 / 60 / 90

    In the links:

    • Step 1 is already done, and the links open on Snippets with the snippet active.
    • Playground shows the fatal error as a blank page and doesn't log it, so Tools → QA Logs stays quiet on the release. On a regular site with logging, debug.log shows the error above.

    Reproduce on Neve 4.2.16:

    1. Snippets → Add New: title "QA: Header row height arrives decoded", paste the code below, then Save and Activate.
      add_action( 'customize_register', function ( $wp_customize ) {
      	$wp_customize->set_post_value( 'hfg_header_layout_main_height', array( 'mobile' => 60, 'tablet' => 60, 'desktop' => 90 ) );
      }, 1000 );
    2. Go to Appearance → Customize.
    • Bug: an empty white page instead of the Customizer.
    • Fixed: the Customizer opens normally with the site preview.
    Edge cases
    • Snippet deactivated: change Header Main → Style → Row height and Border Width, then Publish: they save and show on the front end as before.
    • Snippet deactivated: change a component's responsive padding (for example the Primary Menu's), Publish, reload the Customizer: the value is kept.
    • With Neve Pro: create a conditional header, change a responsive option on it (for example a component padding), then reopen the Customizer and change the same option: the preview refreshes instead of erroring.
    • With Neve Pro: the site front end with a conditional header active shows that header's row heights and paddings.

    Fix: #4662 (open)

  4. pirate-bot commented on Oct 17, 2026

    @pirate-bot
    CollaboratorAuthor

    QA agent — 🟢 Passing checks · The array-input crash is fixed, and responsive header settings still save and render.

    An equivalent native-array fixture reproduced the reported TypeError and HTTP 500 on the base build; the candidate loaded a usable Customizer and preview on both PHP versions. Free-theme and Pro conditional-header settings saved, survived reopening, and rendered correctly at desktop, tablet and mobile widths. Pro testing used a supported seeded license state, not real activation. The added tests and expanded sanitizer/save matrices passed.

    Worth a human look

    1. Try the original customer setting and payload if they become available; this run used an equivalent native-array fixture.
    Checks (7 passed)
    • ✅ With Neve Pro, a conditional header with a stored responsive HFG value can be reopened and the same option changed with a working preview instead of an error.
    • ✅ After publishing a conditional header's responsive option, reopening the Customizer retains the saved responsive values.
    • ✅ The front end renders the published conditional header's responsive row height and component spacing.
    • ✅ A native-array responsive row-height value passed through the registered Customizer sanitizer loads a usable Customizer and preview with the sanitized values rather than a TypeError.
    • ✅ Without Pro, changing responsive row height, border width and component padding in the Customizer publishes and renders the chosen values in the preview and front end.
    • ✅ Without Pro, published responsive HFG values remain intact after reloading the Customizer.
    • ✅ Both JSON sanitizers accept native arrays, preserve valid serialized-string behavior, and return their documented defaults for empty or unsupported non-string inputs without throwing.

    Full report, evidence and session trail

  5. pirate-bot commented on Oct 21, 2026

    @pirate-bot
    CollaboratorAuthor

    🎉 This issue has been resolved in version 4.2.17 🎉

    The release is available on GitHub release

    Your semantic-release bot 📦🚀

  6. added
    releasedIndicate that an issue has been resolved and released in a particular version of the product.
    on Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions