Skip to content

COLDBOX-1468 Answer 400 when a toAi() body is not a JSON object - #728

Merged
lmajano merged 4 commits into
developmentfrom
claude/COLDBOX-1468-toai-body-400
Oct 23, 2026
Merged

lmajano merged 4 commits into
developmentfrom
claude/COLDBOX-1468-toai-body-400

Conversation

@lmajano

@lmajano lmajano commented Oct 23, 2026

Copy link
Copy Markdown
Member

Description

toAi() routes ({base}/invoke, {base}/stream, {base}/batch) answered HTTP 500 when the request had no body, or the body was not a JSON object. event.getHTTPContent( json: true ) returns an empty string for no body or invalid JSON (and an array or string for those JSON types), and resolveAiContext( required struct body ) then failed with a type error. Introduced with COLDBOX-1417.

They now answer 400 with { "error": true, "message": "The request body must be a JSON object" } for: no body, invalid JSON, a JSON array and a JSON string. A valid object body behaves exactly as before.

The check lives in two private helpers, resolveAiBody() and rejectAiBody(), shared by the three routes.

Jira Issues

https://ortussolutions.atlassian.net/browse/COLDBOX-1468

Type of change

  • Bug Fix

HTTP verb finding (not changed here)

The routes declare "verbs": "POST", but a PUT with a valid body also returns 200. Cause:

  • RoutingService.cfc only sets isInvalidHTTPMethod on the event for a verb mismatch.
  • Controller.runEvent is the only place that acts on that flag, and it only runs for handler events.
  • Routes with a response (closure) go straight to renderResponse(), so the verb is never checked.

This affects every response route (toResponse(), closure responses, toMCP(), toAiGateway()), not only toAi(). Enforcing it (405) would change behavior for all of them, so I left it out of this fix. Proposal: a follow-up ticket to enforce verbs for response routes in RoutingService, or if you prefer a narrow fix, a 405 check inside the three toAi closures.

Testing

RouterAITest.cfc: 12 new specs (4 bad bodies x invoke/stream/batch) plus the unchanged valid-body case for invoke and batch. Without the fix the 12 reject specs error; with it all 34 pass. RouterTest, RouterGatewayTest and RouterSSETest still pass. Run on BoxLang 1.18.0 with bx-ai 3.6.0. The stream route is only covered for the 400 cases, since a valid stream needs the SSE() BIF.

Not run locally: cfformat (CommandBox could not reach ForgeBox through the sandbox proxy), so formatting was matched by hand. CI will check it.

Checklist

  • My code follows the style guidelines of this project cfformat (matched by hand, see above)
  • I have commented my code, particularly in hard-to-understand areas
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes (routing specs)
  • Changelog entry added

Generated by Claude Code

The invoke, stream and batch routes passed the result of
getHTTPContent( json: true ) straight to resolveAiContext( struct ).
A missing body, invalid JSON, a JSON array or a JSON string made that
a type error and the request ended as a 500. They now share one body
check and answer 400 with { "error": true, "message": ... }.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJdN5wqon9WEbCykxdPcNN
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJdN5wqon9WEbCykxdPcNN
The local cfformat reflowed a line outside this change, which the CI
formatter rejects. Restore it and keep the 400 payload in a local
variable like the other renderData calls in the router.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJdN5wqon9WEbCykxdPcNN

lmajano commented Oct 23, 2026

Copy link
Copy Markdown
Member Author

CI status on this PR so far: the formatting check was mine and is fixed in 15433cb. The two remaining test failures are not caused by this change:

  • lucee@5: tests.specs.async.tasks.ScheduledTaskSpec ("can align every() + startOnTime()..."), a timing-sensitive scheduler spec. It passed on lucee@6 and every BoxLang job.
  • adobe@2025: tests.specs.web.services.ModuleServiceTest, "Still resolves a handler-only URL for a module with only a mandatory-action route" (test-module-conventions not loaded). That spec arrived with the module convention routing fix on development (17f7faa). development itself alternates between pass and fail at the same commit.

This PR only touches the toAi() routes in Router.cfc (invoke, stream, batch), their spec, and the changelog. The new specs are BoxLang-only, so they are skipped on Lucee and Adobe. I'll re-check the run on 15433cb and re-run any failed job once; no fix for these two exists yet.


Generated by Claude Code

@lmajano
lmajano merged commit 59c633f into development Oct 23, 2026
18 of 20 checks passed
@lmajano
lmajano deleted the claude/COLDBOX-1468-toai-body-400 branch October 23, 2026 16:43
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