Skip to content

Fix fatal error in Stream connector on new follower#3372

Merged
pfefferle merged 2 commits into
trunkfrom
fix/stream-connector-follow-fatal
Jun 7, 2026
Merged

Fix fatal error in Stream connector on new follower#3372
pfefferle merged 2 commits into
trunkfrom
fix/stream-connector-follow-fatal

Conversation

@pfefferle

Copy link
Copy Markdown
Member

Proposed changes:

Fixes a fatal error on every inbound Follow for any site running the Stream plugin.

Since 7.5.0, the activitypub_handled_follow hook passes its user IDs as an array (int[]). The Stream integration's callback_activitypub_handled_follow() still treated that argument as a scalar and forwarded the array straight into WP_Stream\Connector::log()'s $user_id parameter. Stream then built a WP_User from the array:

PHP Fatal error: Uncaught TypeError: trim(): Argument #1 ($string) must be of type string, array given
  …/class-wp-user.php(160): WP_User::get_data_by('login', Array)
  …/stream/classes/class-log.php(78): WP_User->__construct(0)
  …/activitypub/integration/stream/class-connector.php(122): WP_Stream\Connector->log(…, Array)
  …/activitypub/includes/handler/class-follow.php(83): do_action('activitypub_handled_follow', …)

The fix normalizes the user IDs to a single integer in the callback (\is_array( $user_ids ) ? \reset( $user_ids ) : $user_ids), mirroring the pattern already used by every other consumer of this hook (Follow::queue_accept, Mailer::new_follower, Event_Stream::signal_inbox). I audited all consumers of the (array) $user_ids hooks — the Stream follow callback was the only one mishandling the array.

Other information:

  • Have you written new tests for your changes, if applicable? (Updated the Stream connector tests to pass the array the hook actually emits; they fail without the fix.)

Testing instructions:

  • With the Stream plugin active, have a remote account follow your site (or fire do_action( 'activitypub_handled_follow', $activity, array( 1 ), true, $remote_actor )).
  • Before this change: a fatal TypeError in class-wp-user.php. After: the follow is logged to Stream as "New Follower" with no error.
  • npm run env-test -- --filter='Follow|Stream' → all green.

Changelog entry

A changelog entry is already included in this PR (.github/changelog/fix-stream-follower-fatal, patch / fixed), so the automatic entry box is left unchecked.

The activitypub_handled_follow hook passes the user IDs as an array
(int[]) since 7.5.0, but Stream's callback_activitypub_handled_follow
forwarded that array straight into WP_Stream\Connector::log()'s
$user_id parameter. Stream then built a WP_User from the array, which
triggered an uncaught TypeError (trim() on an array) and fataled every
inbound Follow on sites with the Stream plugin active.

Normalize the user IDs to a single integer in the callback, mirroring
the pattern already used by the Follow handler, Mailer, and Event Stream
consumers. Update the existing tests to pass the array the hook actually
emits and assert the logged user ID is a scalar int.
Copilot AI review requested due to automatic review settings June 5, 2026 21:38
@pfefferle pfefferle added the Bug Something isn't working label Jun 5, 2026
@pfefferle pfefferle self-assigned this Jun 5, 2026
@pfefferle pfefferle requested a review from a team June 5, 2026 21:38
@pfefferle

Copy link
Copy Markdown
Member Author

This likely resolves the user report at https://wordpress.org/support/topic/follow-activity-reverts-from-unfollow-to-confirm-request/.

Why the symptom matches: the TypeError here is uncaught, so the inbox POST returns HTTP 500. The remote server never receives a clean response for the Follow, so the Accept handshake never completes and the follow button reverts from "Unfollow" back to "Confirm request." Production logs from an affected (Stream-active) site show this exact fatal firing on activitypub_handled_follow.

Caveat: this only explains the report if the affected site has the Stream plugin active — the fatal originates in the Stream connector. Sites without Stream would need a different explanation.

@github-actions github-actions Bot added [Focus] Compatibility Ensuring the plugin plays well with other plugins [Tests] Includes Tests labels Jun 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a fatal error in the Stream integration when handling inbound Follow activities by aligning the Stream connector with the activitypub_handled_follow hook’s current signature (user IDs passed as an int[] since 7.5.0).

Changes:

  • Update Stream connector follow callback to normalize the hook’s user IDs argument before logging.
  • Adjust Stream connector integration tests to pass the array form the hook actually emits.
  • Add a patch-level changelogger entry documenting the fix.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
integration/stream/class-connector.php Normalizes user_ids for Stream logging to avoid passing an array into Stream’s user resolution.
tests/phpunit/tests/integration/class-test-stream-connector.php Updates tests to call the callback with int[] and asserts Stream receives a scalar user ID.
.github/changelog/fix-stream-follower-fatal Adds a patch/fixed changelog entry for the Stream follower fatal error.

Comment thread integration/stream/class-connector.php Outdated
Comment thread integration/stream/class-connector.php Outdated
- Fix the callback docblock: the third arg is the boolean success flag
  and $context is WP_Post|WP_Error (not WP_Post|null).
- Cast the resolved user ID to int so Stream's log() (which builds a
  WP_User) always receives a scalar, even for an empty user IDs array.
@pfefferle

Copy link
Copy Markdown
Member Author

Thanks @copilot — both addressed in 260fa27:

  1. Docblock — corrected: the third argument is the boolean $success flag, and $context is \WP_Post|\WP_Error (the callback already guards with is_wp_error()), not \WP_Post|null.
  2. Integer coercion — the resolved user ID is now cast to int, so Stream's log() (which constructs a WP_User) always receives a scalar even if the user IDs array is unexpectedly empty.

CI is green across PHP 7.4 / 8.3 / 8.5.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@pfefferle pfefferle merged commit 6e5c0d7 into trunk Jun 7, 2026
12 checks passed
@pfefferle pfefferle deleted the fix/stream-connector-follow-fatal branch June 7, 2026 06:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working [Focus] Compatibility Ensuring the plugin plays well with other plugins [Tests] Includes Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants