Filed unassigned and ungraded by the #12620 dev, session session_01UjujZN219uFzBhSYfMykCd, while implementing that card's exhausted-search notice. ⛔ Not graded, not routed. Out of scope for #12620 and deliberately not folded into its PR — that card repairs a discarded message; this is unvalidated input, a different cause with a different fix.
Measured
packages/cli/src/commands/serve.ts. The flag is a string with no coercion or validation:
port: Flags.string({char: 'p',description: 'Server port',default: readEnvWithDeprecation('OS_PORT','PORT',{silent: true})??'3000'}),and its only consumer is a bare parseInt:
constrequestedPort=parseInt(flags.port);
So --port abc (or PORT=abc, or OS_PORT=abc) makes requestedPortNaN, and nothing anywhere refuses it. There is no isNaN, no Number.isInteger, and no range check on this value — grep -n "parseInt(flags.port)\|isNaN\|Number.isInteger" packages/cli/src/commands/serve.ts returns the parseInt line and one unrelated retries guard, and grep -rn "ERR_SOCKET_BAD_PORT\|[Ii]nvalid port" packages/cli/src finds no handling at all (only comments added by #12620).
NaN then flows into the port-conflict policy and, past it, into the real listen(). Node refuses it, but only at the socket layer:
$ node -e "require('net').createServer().listen(NaN)"
ERR_SOCKET_BAD_PORT # options.port should be >= 0 and < 65536. Received type number (NaN).
⚠️ Same for a numerically-valid-looking but out-of-range value: --port 99999 parses fine and dies the same way.
So the operator's mistake is a typo in a flag, and what they get back is a Node-internal error naming options.port, from a code path with no connection to the flag they typed.
Why this is a sibling of the port-legibility family, not a duplicate
#12620 is about the dev auto-shift search throwing an accurate message that the caller then discarded. This one never produces an accurate message in the first place: the value is wrong before any search runs. The two touch the same lines but neither repair fixes the other — #12620's notice will now carry the ERR_SOCKET_BAD_PORT text on the auto-shift path (that much is better than silence), but a boot that never enters that branch, and every non-dev boot, still dies raw.
Not established here
- Severity not judged, and no claim is made about how often anyone mistypes a port.
- ⚠️ Whether the right repair is a validating flag (
Flags.integer({ min: 0, max: 65535 }), which oclif supports and which would refuse at parse time with the flag's own name), or an explicit refusal beside the existing port-policy block, is not decided here. The first is smaller and fails earlier; the second keeps the wording next to its siblings. That is a real choice and it belongs to whoever takes this.
Re-check
git grep -n "port: Flags.string" origin/main -- packages/cli/src/commands/serve.ts
git grep -n "parseInt(flags.port)" origin/main -- packages/cli/src/commands/serve.ts
node -e "require('net').createServer().listen(NaN)"
Dedup
Searched issues for port validation / parseInt / NaN on serve: no match. The known port-family cards are all about conflict rather than input: #12543 (drift notice), #12620 (exhausted search), #11113 (production no-auto-select), #12441 (e2e port draw), #12525 / #12526 / #12548 (consumer-side, closed), #10167 (a TOCTOU probe in a different subsystem). None covers an invalid port value.
⚠️ One qualifier on that sweep: a follow-up query hit API rate limit already exceeded on this container, so the dedup rests on the one search that did return (0 results) plus the local greps and the family list above, not on an exhaustive scan.
Generated by Claude Code
Filed unassigned and ungraded by the #12620 dev, session
session_01UjujZN219uFzBhSYfMykCd, while implementing that card's exhausted-search notice. ⛔ Not graded, not routed. Out of scope for #12620 and deliberately not folded into its PR — that card repairs a discarded message; this is unvalidated input, a different cause with a different fix.Measured
packages/cli/src/commands/serve.ts. The flag is a string with no coercion or validation:and its only consumer is a bare
parseInt:So
--port abc(orPORT=abc, orOS_PORT=abc) makesrequestedPortNaN, and nothing anywhere refuses it. There is noisNaN, noNumber.isInteger, and no range check on this value —grep -n "parseInt(flags.port)\|isNaN\|Number.isInteger" packages/cli/src/commands/serve.tsreturns theparseIntline and one unrelatedretriesguard, andgrep -rn "ERR_SOCKET_BAD_PORT\|[Ii]nvalid port" packages/cli/srcfinds no handling at all (only comments added by #12620).NaNthen flows into the port-conflict policy and, past it, into the reallisten(). Node refuses it, but only at the socket layer:--port 99999parses fine and dies the same way.So the operator's mistake is a typo in a flag, and what they get back is a Node-internal error naming
options.port, from a code path with no connection to the flag they typed.Why this is a sibling of the port-legibility family, not a duplicate
#12620 is about the dev auto-shift search throwing an accurate message that the caller then discarded. This one never produces an accurate message in the first place: the value is wrong before any search runs. The two touch the same lines but neither repair fixes the other — #12620's notice will now carry the
ERR_SOCKET_BAD_PORTtext on the auto-shift path (that much is better than silence), but a boot that never enters that branch, and every non-dev boot, still dies raw.Not established here
Flags.integer({ min: 0, max: 65535 }), which oclif supports and which would refuse at parse time with the flag's own name), or an explicit refusal beside the existing port-policy block, is not decided here. The first is smaller and fails earlier; the second keeps the wording next to its siblings. That is a real choice and it belongs to whoever takes this.Re-check
Dedup
Searched issues for port validation /
parseInt/ NaN onserve: no match. The known port-family cards are all about conflict rather than input: #12543 (drift notice), #12620 (exhausted search), #11113 (production no-auto-select), #12441 (e2e port draw), #12525 / #12526 / #12548 (consumer-side, closed), #10167 (a TOCTOU probe in a different subsystem). None covers an invalid port value.API rate limit already exceededon this container, so the dedup rests on the one search that did return (0 results) plus the local greps and the family list above, not on an exhaustive scan.Generated by Claude Code