Skip to content

fix(core): escape characters SQS rejects in queued message bodies - #737

Open
d-klotz wants to merge 1 commit into
nextfrom
fix/queuer-sqs-safe-body
Open

d-klotz wants to merge 1 commit into
nextfrom
fix/queuer-sqs-safe-body

Conversation

@d-klotz

@d-klotz d-klotz commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

SQS rejects a message whose body contains a character outside its allowed set (#x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]) with InvalidMessageContents. JSON.stringify escapes control characters and lone surrogates, but leaves U+FFFE and U+FFFF raw. A webhook payload carrying one of them was never queued: queueWebhook threw, the receive handler answered 500, and every provider retry failed the same way, so the event was lost.

Seen in production on one integration's webhook receiver: Invalid binary character '#xFFFE' was found in the message body from QueuerUtil.send.

What changes

  • New queues/sqs-message-body.js → toSqsMessageBody(message): JSON.stringify, then escape every character outside the SQS allowed set as \uXXXX. Those characters can only occur inside JSON string literals (the JSON structure is ASCII), so the result is still valid JSON and JSON.parse returns the original message exactly: lossless, no replacement characters.
  • Used by QueuerUtil.send, QueuerUtil.batchSend and Worker.send, the three places core writes a JSON body to SQS. A body with no such characters is byte-identical to before.

Tests

  • Written red first on next:
    • QueuerUtil.send / batchSend with U+FFFE, U+FFFF (also in a key), a lone surrogate, an emoji, tab and newline. Each asserts the captured SendMessage body has no SQS-disallowed character and parses back to the original.
    • Same for Worker.send.
    • An already-safe body is unchanged.
  • packages/core: 205 suites, 2750 tests pass (2 skipped, as before).
  • ESLint/Prettier: no new findings in the touched files (their pre-existing warnings and formatting are unchanged); the new module is Prettier-clean.

Not changed

Messages written to SQS outside core (adopter code calling the SDK directly) are not covered.

📦 Published PR as canary version: 2.0.0--canary.737.83b4e17.0

✨ Test out this PR locally via:

npm install @friggframework/admin-scripts@2.0.0--canary.737.83b4e17.0
npm install @friggframework/core@2.0.0--canary.737.83b4e17.0
npm install @friggframework/devtools@2.0.0--canary.737.83b4e17.0
npm install @friggframework/eslint-config@2.0.0--canary.737.83b4e17.0
npm install @friggframework/prettier-config@2.0.0--canary.737.83b4e17.0
npm install @friggframework/schemas@2.0.0--canary.737.83b4e17.0
npm install @friggframework/serverless-plugin@2.0.0--canary.737.83b4e17.0
npm install @friggframework/test@2.0.0--canary.737.83b4e17.0
npm install @friggframework/ui@2.0.0--canary.737.83b4e17.0
# or 
yarn add @friggframework/admin-scripts@2.0.0--canary.737.83b4e17.0
yarn add @friggframework/core@2.0.0--canary.737.83b4e17.0
yarn add @friggframework/devtools@2.0.0--canary.737.83b4e17.0
yarn add @friggframework/eslint-config@2.0.0--canary.737.83b4e17.0
yarn add @friggframework/prettier-config@2.0.0--canary.737.83b4e17.0
yarn add @friggframework/schemas@2.0.0--canary.737.83b4e17.0
yarn add @friggframework/serverless-plugin@2.0.0--canary.737.83b4e17.0
yarn add @friggframework/test@2.0.0--canary.737.83b4e17.0
yarn add @friggframework/ui@2.0.0--canary.737.83b4e17.0

JSON.stringify leaves U+FFFE and U+FFFF raw, and SQS rejects any message
containing them with InvalidMessageContents. A webhook payload carrying
one of them was never queued: the receive handler answered 500 and every
provider retry failed the same way.

QueuerUtil.send, QueuerUtil.batchSend and Worker.send now encode bodies
with toSqsMessageBody, which escapes characters outside the SQS allowed
set as \uXXXX. They can only appear inside JSON strings, so the JSON
stays valid and JSON.parse returns the original message exactly.

Co-authored-by: OpenBot <335604065+openbotgit[bot]@users.noreply.github.com>
@d-klotz d-klotz added release Create a release when this pr is merged prerelease This change is available in a prerelease. agent:left-hook-swe PR opened by the Left Hook SWE agent labels Nov 13, 2026

@claude claude Bot 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.

⚠️ Code review skipped — your organization has no extra usage available to pay for this review.

If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.

Once extra usage is available, someone with write access to this repository can comment @claude review on this pull request to trigger a review.

@netlify

netlify Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for friggframework-org canceled.

Name Link
🔨 Latest commit 83b4e17
🔍 Latest deploy log https://app.netlify.com/projects/friggframework-org/deploys/6af6dad375fefde91f38c225

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

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

Labels

agent:left-hook-swe PR opened by the Left Hook SWE agent prerelease This change is available in a prerelease. release Create a release when this pr is merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant