You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(cli): Fix waitPort to ensure FRAMEWORK_PORT_TIMEOUT_MS is respected - #8611
netlify dev now waits the full 10-minute FRAMEWORK_PORT_TIMEOUT_MS for the framework port, instead of giving up after ~15.5s because of a hidden retry cap.
src/lib/wait-port.ts: the loop now ends only when timeout elapses, not after a fixed number of retries. Removed the maxRetries parameter. Back-off, socket handling and ECONNRESET handling are unchanged.
src/utils/framework-server.ts: removed the maxRetries argument (20).
waitPort(port, host, timeout)
- for attempt < maxRetries ?? ceil(timeout / 2000)+ for each attempt
if elapsed > timeout → { open: false }
try connect → { open: true }
catch → sleep min(100ms × attempt, 1s)
Why maxRetries was removed
#7772 replaced the wait-port package, which loops until timeout, and added maxRetries to try to fix CI tests that were timing out. When the port is closed, each attempt fails instantly, so 20 retries give out after ~15.5s, long before the 10-minute timeout. Without the cap, the default of ceil(timeout / 2000) retries still stops at ~4.9 min. No caller needs a retry count, so timeout is now the only limit.
Test trade-off:http-agent.ts was hit by the same cap: its 50s AGENT_PORT_TIMEOUT actually ended after ~20s. Now that it waits the full 50s, the existing test should return error when proxy is not available (unresolvable host) takes ~50s instead of ~20s. That's still within vitest's 90s testTimeout. I kept this PR to the timeout fix and didn't add fail-fast handling for unresolvable hosts. If the slower test is a problem for CI, it could mock waitPort, or ENOTFOUND could fail fast like the old wait-port package did. Happy to do either in a follow-up.
Repro
Run dev with: [dev] command = "sleep 20 && python3 -m http.server 3999", targetPort = 3999
Before:Error: Timed out waiting for port '3999' to be open after ~15s
After:✔ #custom dev server ready on port 3999 at ~23s, proxy returns 200
For us to review and ship your PR efficiently, please perform the following steps:
Open a bug/issue before writing your code 🧑💻. This ensures we
can discuss the changes and get feedback from everyone that should be involved. If you`re fixing a typo or
something that`s on fire 🔥 (e.g. incident related), you can skip this step.
Read the contribution guidelines 📖. This ensures your code follows our style guide and
passes our tests.
Update or add tests (if any source code was changed or added) 🧪
Update or add documentation (if features were changed or added) 📝
Make sure the status checks below are successful ✅
A picture of a cute animal (not mandatory, but encouraged)
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.
📝 Summary
Summary by CodeRabbit
Bug Fixes
Improved startup handling when a framework server’s port is not immediately available. The server now waits up to 10 minutes for the port to open, rather than checking for only a fraction of a second.
Port checks now report whether a connection succeeds or the configured wait period expires, including when the connection is reset.
📝 Summary
Walkthrough
waitPort no longer uses a retry-count limit. It retries until a connection succeeds or the timeout expires. The framework server now uses a 10-minute timeout. New unit tests cover delayed connections, reset connections, and ports that do not open.
Frameworks that bind their port late can now use the configured startup window. No actionable merge-blocking risk was identified.
Security Architecture Review
Security architecture risk:🔵 Low · up to 326b8
Longer waits preserve the existing connection checks and failure handling. No new destination or privilege is introduced, but readiness still accepts any listener at the configured address. Whether the longer window matters in environments shared with untrusted processes remains uncertain.
Retained concerns
No architecture-level concerns identified.
Security review details
Security Blast Radius
inferred — The inspected exposure is bounded to the configured framework loopback port and configured proxy destination within the invoking CLI process. Longer retries change timing, not destination selection or authority. Broader deployment and tenant isolation were not established.
Trust Boundaries and Controls
observed — Framework probes remain directed to localhost, and unavailable ports still cause startup failure. Proxy URL parsing, scheme restrictions and failure responses remain unchanged. These checks constrain configuration and availability; they do not authenticate the service answering the readiness probe.
Resilience and Maintainability Implications
observed — Attempts remain sequential, with per-call state rather than shared retry ownership. There is no cancellation interface in either revision, so callers abandoning a wait cannot explicitly stop it; the longer effective wait can prolong that inherited behavior.
Hardening Proposals
proposed — If development runs alongside untrusted local processes, consider application-specific readiness or process isolation rather than treating TCP availability as service identity. This is conditional hardening, not an established PR-introduced vulnerability.
Pre-merge checks | 5
✅ Passed checks (5 passed)
Check name
Status
Explanation
Linked Issues check
Issue #8610 requires framework-port waiting to respect the configured timeout instead of a fixed retry cap. src/lib/wait-port.ts now retries until the elapsed timeout check returns { open: false }…
Out of Scope Changes check
The changes are limited to the waitPort retry behavior, its framework-server call site, and focused unit tests. These changes directly implement issue #8610 and provide regression coverage. No unrel…
Docstring Coverage
No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
Description check
The description clearly explains the waitPort timeout fix, the affected files, the tests, and the known test trade-off. It is directly related to the changeset.
Title check
The title clearly identifies the waitPort fix and the goal of respecting FRAMEWORK_PORT_TIMEOUT_MS.
✨ Finishing Touches
🧪 Generate unit tests (beta)
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Comment @coderabbitai help to get the list of available commands.
McLeodSean
changed the title
Fix waitPort to ensure FRAMEWORK_PORT_TIMEOUT_MS is respected
fix(cli): Fix waitPort to ensure FRAMEWORK_PORT_TIMEOUT_MS is respected
Oct 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #8610
netlify devnow waits the full 10-minuteFRAMEWORK_PORT_TIMEOUT_MSfor the framework port, instead of giving up after ~15.5s because of a hidden retry cap.src/lib/wait-port.ts: the loop now ends only whentimeoutelapses, not after a fixed number of retries. Removed themaxRetriesparameter. Back-off, socket handling andECONNRESEThandling are unchanged.src/utils/framework-server.ts: removed themaxRetriesargument (20).tests/unit/lib/wait-port.test.ts(new): covers a late-opening port, a port that never opens, and a server that resets the connection (guards the ECONNRESET crash when using Netlify CLI with Node.js 24.x and Nuxt 4.x dev server #7747 fix).Why
maxRetrieswas removed#7772 replaced the
wait-portpackage, which loops untiltimeout, and addedmaxRetriesto try to fix CI tests that were timing out. When the port is closed, each attempt fails instantly, so 20 retries give out after ~15.5s, long before the 10-minute timeout. Without the cap, the default ofceil(timeout / 2000)retries still stops at ~4.9 min. No caller needs a retry count, sotimeoutis now the only limit.Test trade-off:
http-agent.tswas hit by the same cap: its 50sAGENT_PORT_TIMEOUTactually ended after ~20s. Now that it waits the full 50s, the existing testshould return error when proxy is not available(unresolvable host) takes ~50s instead of ~20s. That's still within vitest's 90stestTimeout. I kept this PR to the timeout fix and didn't add fail-fast handling for unresolvable hosts. If the slower test is a problem for CI, it could mockwaitPort, orENOTFOUNDcould fail fast like the oldwait-portpackage did. Happy to do either in a follow-up.Repro
Run dev with:
[dev] command = "sleep 20 && python3 -m http.server 3999",targetPort = 3999Error: Timed out waiting for port '3999' to be openafter ~15s✔ #custom dev server ready on port 3999at ~23s, proxy returns 200For us to review and ship your PR efficiently, please perform the following steps:
can discuss the changes and get feedback from everyone that should be involved. If you`re fixing a typo or
something that`s on fire 🔥 (e.g. incident related), you can skip this step.
passes our tests.
A picture of a cute animal (not mandatory, but encouraged)