Skip to content

943 seo data optimization error reporting refactoring#23402

Open
pls78 wants to merge 12 commits into
trunkfrom
943-seo-data-optimization-error-reporting-refactoring
Open

943 seo data optimization error reporting refactoring#23402
pls78 wants to merge 12 commits into
trunkfrom
943-seo-data-optimization-error-reporting-refactoring

Conversation

@pls78

@pls78 pls78 commented Jun 24, 2026

Copy link
Copy Markdown
Member

Context

When an unexpected error occurred while building an indexable during SEO data optimization, the failure surfaced as an opaque error: the user had no indication of which object could not be optimized. This PR carries the failing object's identity (type + ID) from the indexable builder all the way through to the WP-CLI output and the REST/React error UI, and gives third parties a hook to react to the failure.

Summary

This PR can be summarized in the following changelog entry:

  • Adds the failing object's type and ID to the SEO data optimization error report when an indexable cannot be built.

Relevant technical choices:

  • A new Indexing_Failed_Exception (extending Indexable_Exception) wraps the underlying throwable and carries the object's object_id, object_type, and object_sub_type.
  • The indexable builder logs the error and fires a new wpseo_indexable_indexing_failed action so third parties can react to a failed indexable build.
  • The JS indexation engine was extracted into a shared AbstractIndexation component so the Tools page and the first-time configuration share a single code path, with the render layer kept in thin subclasses.

Test instructions

Test instructions for the acceptance test before the PR gets merged

This PR changes what the user sees when an indexable cannot be built during SEO data optimization. To test it you need to (a) make sure indexing actually runs, (b) force a build failure, then (c) check the CLI output and the React error UI. Finally, confirm the normal (happy-path) optimization still works once the forced failure is removed.

Setup

  • Use a site with a handful of published posts.
  • Make sure the SEO optimization hasn't been performed (or reset the indexables with the Yoast Test Helper)

Force a build failure (temporary code change)

  • Open src/builders/indexable-post-builder.php and, as the very first line of public function build( $post_id, $indexable ) (around line 115), add:
    throw new \Exception( 'Simulated indexing failure' );
    Every post indexable will now throw an unexpected error, which Indexable_Builder::build() catches, logs, fires wpseo_indexable_indexing_failed for, and re-throws as Indexing_Failed_Exception — exactly the path this PR adds, with object type post and a real post ID.

Check the React error UI (Tools page)

  • Go to Yoast SEO → Tools and open your browser's developer console.
  • Start SEO data optimization ("Optimize SEO data" / "Start SEO data optimization").
  • An error alert appears. Click "Error details" to expand it and confirm the first line reads "Failing object" with the value post #{id} (the ID of the post that failed). The Request URL, Request method, Status code, Error message, and a collapsible "Error stack trace" should also be present.
  • Confirm there are no uncaught JavaScript errors in the console.

Check the WP-CLI output

  • Run the indexing command from the CLI: wp yoast index (in this worktree-based Docker setup, run it inside the cli container or via wp-env run cli wp yoast index).
  • Confirm it prints Could not optimize post #{id} while indexing posts: Simulated indexing failure and stops with an error, instead of an opaque failure.

(Optional) Third-party hook + log

  • Drop this into a mu-plugin and confirm it fires once per failed object:
    add_action( 'wpseo_indexable_indexing_failed', function ( $id, $type, $sub_type, $e ) {
        error_log( "Yoast indexing failed: {$type} #{$id}" . $e->getMessage() );
    }, 10, 4 );
    With WP_DEBUG_LOG enabled, the builder's own logger also writes the failure to debug.log.

Confirm the happy path still works (no regression)

  • Remove the temporary throw (and the optional hook).
  • Re-run SEO data optimization on the Tools page and confirm it completes successfully end to end.
  • Run the first-time configuration and confirm its indexing step still completes — it now shares the same engine (AbstractIndexation) as the Tools page.

Note on automated tests: the unit suites (composer test, yarn test), linting, and composer check-branch-cs all pass locally. The WordPress integration suite was run locally from a git worktree; the only failures (5) are environmental — they assert the canonical wordpress-seo plugin folder name but the worktree mounts the plugin under a different folder, so the asserted plugin URL/path differs. None touch the code in this PR. CI is the source of truth for integration tests.

Relevant test scenarios

  • Changes should be tested with the browser console open
  • Changes should be tested on different posts/pages/taxonomies/custom post types/custom taxonomies
  • Changes should be tested on different editors (Default Block/Gutenberg/Classic/Elementor/other)
  • Changes should be tested on different browsers
  • Changes should be tested on multisite
  • Browser console: with the console open, verify the error UI expands "Error details" and renders the "Failing object" line without throwing any JS errors.
  • Different object types: the "Failing object" line reports {object_type} #{object_id} for whichever indexable fails — to see a non-post type, move the temporary throw into the matching builder (e.g. indexable-term-builder.php for term #{id}).

Test instructions for QA when the code is in the RC

  • QA should use the same steps as above.

Forcing a build failure requires a temporary code change, so QA can focus on the happy path plus a visual check of the error UI:

  1. On a site with content, run the normal Yoast SEO → Tools → SEO data optimization flow to completion and confirm it still succeeds end to end (no regression in the happy path).
  2. Run the first-time configuration and confirm its indexing step still runs and completes — it now shares the same engine as the Tools page.
  3. Confirm the wp yoast index WP-CLI command still indexes content normally and reports completion.
  4. If a reproducible indexing failure can be produced (see the developer steps above — add a temporary throw in indexable-post-builder.php), confirm the Tools-page error alert's "Error details" shows a "Failing object" line as post #{id}, and that wp yoast index prints Could not optimize post #{id} while indexing posts: …. Remove the temporary change afterward.

Impact check

This PR affects the following parts of the plugin, which may require extra testing:

  • The indexable building path (Indexable_Builder) — now logs, fires wpseo_indexable_indexing_failed, and re-throws as Indexing_Failed_Exception on unexpected errors.
  • The wp yoast index WP-CLI command and the indexing REST route — both now handle the new exception.
  • The SEO data optimization UI on the Tools page and the first-time configuration indexing step — both refactored onto the shared AbstractIndexation engine, so the happy-path indexing flow should be regression-tested in both places.

Other environments

  • This PR also affects Shopify. I have added a changelog entry starting with [shopify-seo], added test instructions for Shopify and attached the Shopify label to this PR.
  • This PR also affects Yoast SEO for Google Docs. I have added a changelog entry starting with [yoast-doc-extension], added test instructions for Yoast SEO for Google Docs and attached the Google Docs Add-on label to this PR.

Documentation

  • I have written documentation for this change. For example, comments in the Relevant technical choices, comments in the code, documentation on Confluence / shared Google Drive / Yoast developer portal, or other.

Quality assurance

  • I have tested this code to the best of my abilities.
  • During testing, I had activated all plugins that Yoast SEO provides integrations for.
  • I have added unit tests to verify the code works as intended.
  • If any part of the code is behind a feature flag, my test instructions also cover cases where the feature flag is switched off.
  • I have written this PR in accordance with my team's definition of done.
  • I have checked that the base branch is correctly set.
  • I have run grunt build:images and committed the results, if my PR introduces or edits images or SVGs.

Innovation

  • No innovation project is applicable for this PR.
  • This PR falls under an innovation project. I have attached the innovation label.
  • I have added my hours to the WBSO document.

Fixes #943

@pls78 pls78 added the changelog: enhancement Needs to be included in the 'Enhancements' category in the changelog label Jun 24, 2026
@coveralls

coveralls commented Jun 24, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 908

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Warning

No base build found for commit 5c5487d on trunk.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 45.965%

Details

  • Patch coverage: 9 uncovered changes across 3 files (133 of 142 lines covered, 93.66%).

Uncovered Changes

File Changed Covered %
packages/js/src/components/AbstractIndexation.js 55 48 87.27%
packages/js/src/first-time-configuration/tailwind-components/steps/indexation/indexation.js 1 0 0.0%
src/commands/index-command.php 15 14 93.33%
Total (8 files) 142 133 93.66%

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 61458
Covered Lines: 29617
Line Coverage: 48.19%
Relevant Branches: 11943
Covered Branches: 4122
Branch Coverage: 34.51%
Branches in Coverage %: Yes
Coverage Strength: 7.12 hits per line

💛 - Coveralls

@pls78 pls78 force-pushed the 943-seo-data-optimization-error-reporting-refactoring branch from 600e5a2 to 3ecba73 Compare June 29, 2026 14:09
@pls78 pls78 marked this pull request as ready for review June 29, 2026 14:36
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

A merge conflict has been detected for the proposed code changes in this PR. Please resolve the conflict by either rebasing the PR or merging in changes from the base branch.

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

Labels

changelog: enhancement Needs to be included in the 'Enhancements' category in the changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plugin breaks breadcrumbs in Genesis Framework

3 participants