Skip to content

Fix mailing list member vars encoding: {} for empty, normalize vars in createMultiple() - #961

Open
cheesegrits wants to merge 2 commits into
mailgun:masterfrom
cheesegrits:fix/mailing-list-member-vars-encoding
Open

cheesegrits wants to merge 2 commits into
mailgun:masterfrom
cheesegrits:fix/mailing-list-member-vars-encoding

Conversation

@cheesegrits

Copy link
Copy Markdown

Summary

Two fixes to how MailingList\Member encodes a member's vars. The API requires vars to be a JSON object; both bugs sent something else.

1. create() and update() sent [] for an empty vars array. create() always includes a vars form field, json_encoded from its $vars argument, and the empty default encodes to [], a JSON array. Since 2026-09-25 the API rejects that:

400 The parameters passed to the API were invalid. Check your inputs!
'vars' parameter is not a valid JSON

so every create() call that did not pass vars started failing. update() did the same when handed an empty array. Both now send {} for an empty array (verified against the live API: vars={} and omitting vars are accepted, vars=[] is rejected).

This looks like a change in the API's server-side validation rather than anything in the SDK: our application has run this exact create() call, on this SDK version and its predecessors, for years without a single such error, and the first rejection appeared on 2026-09-25 at about 12:04 UTC with no deploy or dependency change on our side. The status page shows nothing for that day. Whether the array form was ever documented as valid or was merely tolerated, {} is what the API accepts now.

2. createMultiple() never normalized vars. Its per-member loop iterates $members by value, so the inner json_encode of a vars array modified a copy and never reached the payload. Array vars therefore already landed as nested objects, but an empty array went out as [] (rejected as above) and a pre-encoded JSON string was nested as a JSON string, which the API silently skips while answering 200 (verified live: the member is never added). The loop is now by reference and vars are normalized in place: an empty array encodes as {}, a pre-encoded JSON object string is decoded so it lands as an object, and a string that is not a JSON object throws InvalidArgumentException instead of being dropped silently.

Tests

  • testCreate now pins vars={} for the empty default (it previously pinned the rejected []).
  • testCreateWithVars, testUpdateEmptyVars: the non-empty and update paths.
  • testCreateMultipleVarsStayJsonObjects: array, empty array and pre-encoded string vars all land as objects in the members JSON.
  • testCreateMultipleInvalidVarsString: a non-JSON string throws.

Workaround

If you are hitting 'vars' parameter is not a valid JSON on member()->create() and need a fix before this is merged and released, either of these works on the current release with no SDK change:

  • Pass any non-empty $vars array, for example ['source' => 'app']. It encodes to a JSON object, which the API accepts. The downside is that the value is stored on the member.
  • Add the member through createMultiple() with a one-element array instead:
$mailgun->mailingList()->member()->createMultiple(
    $list,
    [['address' => $address, 'name' => $name]],
    true // upsert
);

This sends no vars at all. Do not pass 'vars' => '{}' there on the current release: it is nested as a JSON string and the API silently skips the member (see bug 2).

Notes

Existing callers passing vars as a pre-encoded string to createMultiple() were never actually adding those members, so decoding the string is a behaviour fix rather than a break. Callers passing non-empty arrays anywhere see no change.

Member::create() always sends a `vars` form field, json_encode'd from its
$vars argument, and Member::update() does the same when given an array.
For the empty default that produces `[]`, a JSON array, where the API
requires a JSON object. Since 2026-09-25 the API rejects it:

    400 The parameters passed to the API were invalid. Check your inputs!
    'vars' parameter is not a valid JSON

so every create() call that did not pass vars started failing. Verified
against the live API: `vars={}` and omitting vars are both accepted,
`vars=[]` is rejected.

Both methods now route vars through encodeVars(), which emits `{}` for an
empty array and json_encode otherwise. testCreate pins the new shape,
testCreateWithVars and testUpdateEmptyVars cover the non-empty and the
update paths.
createMultiple() iterated $members by value, so its per-member handling
of `vars` (json_encode of an array) modified a copy and never reached
the request. In practice array vars already landed as nested JSON
objects, which is what the API wants, but two shapes went out wrong:

  - an empty array encoded as `[]`, which the API rejects
    ("'vars' parameter is not a valid JSON", see the previous commit);
  - a pre-encoded JSON string was nested as a JSON string, and the API
    silently skips such a member while answering 200 (verified against
    the live API: the member is never added).

The loop is now by reference and vars are normalized in place: an empty
array becomes an empty object so it encodes as `{}`, a pre-encoded JSON
object string is decoded so it lands as an object, and a string that is
not a JSON object throws InvalidArgumentException instead of being
dropped silently. Non-empty array vars behave exactly as before.
@cheesegrits

Copy link
Copy Markdown
Author

The PHP-Compatibility failure is unrelated to this change: a new PHPCompatibility sniff flags trim() without an explicit characters argument (PHP 8.6 default change) in src/Api/Route.php and src/Message/MessageBuilder.php, neither touched here. All other checks pass.

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