Skip to content

fix(api): Geocoding REST controller fatals on WP_Error — public autocomplete returns HTTP 500 on every short query #759

Description

@chubes4

Summary

Geocoding::search() treats the ability's return value as an array without an is_wp_error() guard. executeGeocodeSearch() is declared array|\WP_Error, so every WP_Error return path fatals instead of becoming a clean 4xx.

This is live on production right now and is user-facing: the city/venue autocomplete fires per keystroke, so every user typing a location triggers a 500 at 1–2 characters before the query becomes valid at 3.

Reproduction (production, verified)

curl -o /dev/null -w "%{http_code}" \
  "https://events.extrachill.com/wp-json/datamachine/v1/events/geocode/search?query=ab"
→ 500

curl "…/events/geocode/search?query=charleston"
→ 200 (valid path works fine)

Root cause

inc/Api/Controllers/Geocoding.php:35

$result = $abilities->executeGeocodeSearch( array( 'query' => ... ) );

if ( ! empty( $result['error'] ) ) {   // ← line 35: $result may be WP_Error

inc/Abilities/GeocodingAbilities.php:196 declares:

public function executeGeocodeSearch( array $input ): array|\WP_Error

and returns WP_Error on two paths:

  • L199-201 — invalid_query, query shorter than 3 characters (already carries status => 400)
  • L214-216 — passthrough of a NominatimClient::searchAddress() transport error

Neither is handled. Cannot use object of type WP_Error as array is thrown at line 35.

Secondary: the $result['error'] check itself is dead code — the success path returns array( 'success' => true, 'results' => ... ) and never sets an error key. The controller is checking for an error shape the ability does not emit, while ignoring the error shape it does.

Evidence from production

17 fatals in the live debug.log since rotation at 00:00 UTC 2026-09-04, all with the identical stack:

PHP Fatal error:  Uncaught Error: Cannot use object of type WP_Error as array in
  .../data-machine-events/inc/Api/Controllers/Geocoding.php:35
#0 wp-includes/rest-api/class-wp-rest-server.php(1293):
     DataMachineEvents\Api\Controllers\Geocoding->search()

Note this signature also appears network-wide in the composed health read across all 10 subsites, because it lands in the shared debug.log.

Expected

The WP_Error should be returned directly — it already carries the correct status (400), so REST renders a proper error response.

$result = $abilities->executeGeocodeSearch( array( 'query' => ... ) );

if ( is_wp_error( $result ) ) {
    return $result;
}

return rest_ensure_response( $result );

Acceptance

  • ?query=ab returns 400 with a JSON error body, not 500
  • ?query=charleston still returns 200 with the results payload
  • No Cannot use object of type WP_Error as array entries in debug.log after deploy
  • A transport-layer Nominatim failure surfaces as its own error response rather than a fatal

Layer note

The fix belongs in the controller, which is the thin REST adapter. The ability's array|\WP_Error contract is correct and should not be changed to accommodate the caller — the adapter is the layer that failed to honor the declared contract.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions