Skip to content

Skip pixels without an access token instead of rejecting the whole event - #19

Merged
loevgaard merged 1 commit into
2.xfrom
skip-pixels-without-access-token
Sep 21, 2026
Merged

loevgaard merged 1 commit into
2.xfrom
skip-pixels-without-access-token

Conversation

@loevgaard

Copy link
Copy Markdown
Member

Summary

Since #17 a single pixel without an access token made the client throw before sending anything. That caught a forgotten withAccessTokens(), but it also meant one token-less pixel kept every other pixel from receiving the event. And a token-less pixel is a documented, legitimate state: the docblock on Pixel::$accessToken lists browser-only tracking as the first reason the token is nullable. A shop with one browser-only pixel next to three fully configured ones got no server events at all.

Behaviour now

  • The client sends to every pixel that has an access token.
  • Pixels without one are skipped, and the client logs one error naming them: The event was not sent to these pixels because they have no access token: pixel_2, pixel_4.
  • It throws InvalidArgumentException only when none of the pixels has an access token. That is what a forgotten PreparedEvent::withAccessTokens() after withoutAccessTokens() looks like, since then every pixel is token-less. Nothing can be delivered, so failing loudly is right, and no request is made.
  • An empty string still counts as missing, and a request without an access token is still never made, since Meta answers it with an error that does not mention the token.

This lines up with how the client already treats an event with no pixels at all: a log line, not an exception.

The trade-off

If a pixel id is missing from the map passed to withAccessTokens(), that pixel is now skipped with an error log instead of stopping everything. The other pixels still get their conversions, and the log names the pixel. A consumer that wants strictness can check the pixels before sending.

Not in this PR

The same principle is violated elsewhere: when Meta answers non-200 for one pixel, the loop throws immediately, so pixels after it never receive the event while pixels before it already have. Attempting all pixels and reporting failures at the end is a bigger change and deserves its own discussion.

Compatibility

No BC break (Roave: none). Relative to v2.0.0-alpha.2 this relaxes behaviour: a mix of pixels with and without access tokens no longer throws.

Docs

README (queue section, the InvalidArgumentException row in the error handling table, the logging section), CLAUDE.md, UPGRADE-2.0.md (section rewritten: "Pixels without an access token are skipped"), and the docblocks on ClientInterface, Pixel::$accessToken and InvalidArgumentException.

Test plan

  • it_skips_the_pixels_without_an_access_token_and_sends_to_the_others — four pixels, one browser-only and one with an empty token: exactly the other two receive the event with their own tokens, and exactly one error is logged naming the skipped two
  • it_throws_when_none_of_the_pixels_has_an_access_token — prepare()->withoutAccessTokens() sent as is: the message lists the pixels, no request made
  • it_treats_an_empty_access_token_as_missing — unchanged, still throws for a single pixel
  • it_logs_nothing_when_all_pixels_have_an_access_token
  • 123 tests, 100 % line coverage; PHPStan (level max), ECS, dependency analyser green; Infection MSI 95 % / covered 95 %, no escaped mutants in the client
  • Roave BC check locally against 2.x: no breaks

Since #17 a single pixel without an access token made the client throw
before sending anything, so that one pixel kept every other pixel from
receiving the event. A pixel without an access token is a legitimate
state, a pixel that is only used in the browser for instance, so a shop
with one such pixel next to fully configured ones got no server events
at all.

The client now sends to every pixel that has an access token, skips the
ones that do not, and logs an error naming them. It throws an
InvalidArgumentException only when none of the pixels has an access
token, which is what happens when PreparedEvent::withAccessTokens() is
forgotten after withoutAccessTokens(): then nothing can be delivered and
it is almost certainly a mistake.

A request without an access token is still never made, since Meta
answers it with an error that does not mention the token.
@loevgaard
loevgaard merged commit 962f4d7 into 2.x Sep 21, 2026
28 checks passed
@loevgaard
loevgaard deleted the skip-pixels-without-access-token branch September 21, 2026 09:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant