abi/screenshot-to-code · critical · RuntimeError
No available port found from {start_port} to {start_port + m
Error message
No available port found from {start_port} to {start_port + max_attempts - 1} What it means
RuntimeError("No available port found from {start} to {end}") raised by find_available_port() in backend/start.py when every port in the inclusive-at-start, exclusive-at-end range fails is_port_available()'s bind() test. start.py uses this to pick a uvicorn port (default 7001, 20 attempts => 7001-7020), so the failure means the backend cannot start at all. bind() fails on OSError, covering ports already bound, ports in exclusive use, and addresses/interfaces that cannot be bound on the chosen --host.
Source
Thrown at backend/start.py:23
def is_port_available(host: str, port: int) -> bool:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
sock.bind((host, port))
except OSError:
return False
return True
def find_available_port(host: str, start_port: int, max_attempts: int) -> int:
for port in range(start_port, start_port + max_attempts):
if is_port_available(host, port):
return port
raise RuntimeError(
f"No available port found from {start_port} to "
f"{start_port + max_attempts - 1}"
)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--host", default="127.0.0.1")
parser.add_argument("--port", type=int, default=7001)
parser.add_argument("--max-port-attempts", type=int, default=20)
args = parser.parse_args()
port = find_available_port(args.host, args.port, args.max_port_attempts)
if port != args.port:
print(f"Port {args.port} is in use. Starting backend on port {port}.")
uvicorn.run("main:app", host=args.host, port=port, reload=True)
View on GitHub (pinned to d026163f58)
Solutions
- Find and kill the squatters: ss -ltnp 'sport >= :7001' (or lsof -i :7001-7020), then kill the stale PIDs
- Start on a different base port: python start.py --port 7100
- Widen the scan: python start.py --max-port-attempts 100
- If --host was set to a specific interface, verify it is up (ip addr) or fall back to the default 127.0.0.1
Example fix
# before python start.py # RuntimeError: No available port found from 7001 to 7020 # after python start.py --port 7100 # or first: kill $(lsof -t -i :7001-7020)
Defensive patterns
Strategy: fallback
Validate before calling
import socket
def ports_free(host: str, start: int, count: int) -> list[int]:
free = []
for port in range(start, start + count):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
s.bind((host, port)); free.append(port)
except OSError:
pass
return free # empty list => start.py will raise RuntimeError Try / catch
try:
port = find_available_port(host, start_port, max_attempts)
except RuntimeError:
port = 7100 # fallback: fixed alternate port, or kill stale servers first (lsof -t -i :7001-7020 | xargs kill) Prevention
- Kill stale uvicorn/start.py instances before starting a new dev session
- Pass --port explicitly in shared or containerized environments rather than relying on the scan
- Map only the ports you need in Docker; avoid publishing wide port ranges that squat 7001-7020
When it happens
Trigger: Starting the backend with defaults while 20 prior dev-server instances (or other apps) hold 7001-7020; passing --host of an interface that is down or not owned by the process; running under an environment where loopback binding is restricted.
Common situations: Repeated `uvicorn main:app --reload` sessions that never got killed; Docker port mappings consuming the range; another service (e.g. airtower/ADS-B on 7001-adjacent ports or a previously orphaned start.py) squatting the range.
AI-assisted analysis of abi/screenshot-to-code@d026163f58 (2026-08-14).
Data as JSON: /api/errors/8117fcfee01ddb82.
Report an issue: GitHub.