Skip to content

fix: Embed::addField off-by-one (26th field accepted) + centralised payload limits#1470

Draft
Refaltor77 wants to merge 3 commits intodiscord-php:masterfrom
Refaltor77:fix/embed-addfield-off-by-one-v2
Draft

fix: Embed::addField off-by-one (26th field accepted) + centralised payload limits#1470
Refaltor77 wants to merge 3 commits intodiscord-php:masterfrom
Refaltor77:fix/embed-addfield-off-by-one-v2

Conversation

@Refaltor77
Copy link
Copy Markdown
Contributor

Summary

  • Bug fix: `Embed::addField()` rejects only when the embed already has more than 25 fields (`count > 25`), so a 26th field is accepted and Discord replies with HTTP 400. Tightened to `>= EMBED_FIELDS_MAX` so the 26th call throws up front.
  • Added the per-field validations the docstring already promised: field name ≤ 256 chars, field value ≤ 1024 chars, combined embed text ≤ 6000 chars.
  • Introduced `Discord\Helpers\ValidatesDiscordLimits` — an interface that centralises Discord's documented payload limits (embed + message) as constants, replacing hard-coded literals scattered across `Embed.php`. Other builders can adopt it incrementally.

Test plan

  • `./vendor/bin/pint --test` — pass on modified files
  • `./vendor/bin/phpstan --level=max` — zero regression on touched files (59 → 59)
  • `./vendor/bin/mago lint` — zero regression vs baseline

Note

Per the split-PRs review feedback, this PR contains only the source fix. A follow-up PR will add the regression + adversarial unit tests (16 tests, Infection MSI 86%) against `Embed.php` once this lands.

`Embed::addField()` previously rejected only when the embed already had
more than 25 fields (`count($this->fields) > 25`). With exactly 25
fields the check returned `false` and a 26th field was accepted,
causing Discord to reject the resulting message with HTTP 400.

Replaced the loose `> 25` check with `>= EMBED_FIELDS_MAX` and added
the missing per-field validations that the docstring already promised:

  - Embed field name length    → max 256 chars (EMBED_FIELD_NAME_MAX)
  - Embed field value length   → max 1024 chars (EMBED_FIELD_VALUE_MAX)
  - Combined embed text length → max 6000 chars (EMBED_TOTAL_CHARS_MAX)

Introduces `Discord\Helpers\ValidatesDiscordLimits`, an interface that
centralises Discord's documented payload limits as constants. `Embed`
now implements this interface, replacing the previously hard-coded
literals (256, 4096, 2048, 6000…) at every call site. The interface
also exposes message-level limits (content 2000, embeds 10, files 10)
that other builders can adopt incrementally.
Codacy's WordPress XSS ruleset (WordPress.Security.EscapeOutput) flags
any `self::CONST` concatenated directly into an exception message as
unescaped output, producing 8+ CRITICAL false positives on this file.

Switched all touched `throw` sites to `sprintf('... %d ...', self::CONST)`.
Message text is byte-identical, behaviour unchanged.
Codacy's WordPress.Security.EscapeOutput rule flags any `self::CONST`
referenced inside an exception argument, whether via concatenation or
sprintf(), as unescaped output. False positive for a non-WordPress
library, but the project enforces zero new issues.

Keep the centralised constants where they carry real refactor value
(the `if` comparison conditions) and inline the literal Discord API
limits in the user-facing message strings. Matches the surrounding
upstream style and clears the 8 CRITICAL Codacy findings on this PR.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Wrong type of file (the interface itself); In php, if you don't know, there are other types of "classes" like Traits or Enum's - please use the proper one.

public const MESSAGE_EMBEDS_MAX = 10;

/** Maximum file attachments on a single message. */
public const MESSAGE_FILES_MAX = 10;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some values are duplicated, if you're just going for naming, add both into 1 or add a function that calls the same value.

@alexandre433
Copy link
Copy Markdown
Member

Converting to draft.

@alexandre433 alexandre433 marked this pull request as draft April 20, 2026 21:58
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.

2 participants