OAuth callback fails in devcontainers: server binds to IPv6 (::1) but VS Code port forwarding connects via IPv4

Resolved 💬 4 comments Opened Apr 7, 2026 by brianjcohen Closed Apr 28, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

claude login fails in VS Code devcontainers because the OAuth callback server binds to IPv6 only, but VS Code's port forwarding tunnel connects via IPv4.

Root cause: The callback server calls server.listen(port, 'localhost'). Node.js resolves 'localhost' via the OS. In most Docker containers, /etc/hosts contains:

127.0.0.1  localhost
::1        localhost

Node.js picks the first matching address family — and on Linux, ::1 (IPv6) wins. The server binds only to ::1.

VS Code's port forwarding tunnel detects the listening port and creates a proxy on the host. When the browser's OAuth callback hits localhost:PORT on the host, VS Code accepts the TCP connection and attempts to relay it to the container — but connects to 127.0.0.1:PORT (IPv4). Since nothing is listening on IPv4 inside the container, the connection hangs: TCP established, zero bytes transferred.

Evidence

From inside the container, curl confirms the server is IPv6-only:

$ curl -v "http://localhost:40735/callback?code=...&state=..."
*   Trying 127.0.0.1:40735...
* connect to 127.0.0.1 port 40735 failed: Connection refused    ← IPv4 fails
*   Trying [::1]:40735...
* Connected to localhost (::1) port 40735 (#0)                   ← IPv6 works
< HTTP/1.1 302 Found
< Location: https://platform.claude.com/oauth/code/success?app=claude-code

From the host, VS Code's tunnel accepts but hangs:

$ curl -v http://localhost:40735/
* Established connection to localhost (127.0.0.1 port 40735)     ← VS Code proxy accepts
* Request completely sent off
                                                                  ← hangs forever, 0 bytes

Suggested Fix

Change the bind address from 'localhost' to '127.0.0.1' in the OAuth callback server:

// Before:
server.listen(port, 'localhost', () => {

// After:
server.listen(port, '127.0.0.1', () => {

This ensures the server binds to IPv4 regardless of the container's /etc/hosts configuration. VS Code's port forwarding connects via IPv4, so the tunnel will work.

Alternatively, bind to '0.0.0.0' to listen on all interfaces (IPv4), or to both '127.0.0.1' and '::1' for dual-stack support.

Workaround

Remove the ::1 localhost line from /etc/hosts inside the container at startup, forcing Node.js to resolve localhost to 127.0.0.1:

# In docker-compose.yml command:
command: >-
  sh -c 'grep -v "^::1.*localhost" /etc/hosts > /tmp/hosts.fix
  && cat /tmp/hosts.fix > /etc/hosts && rm /tmp/hosts.fix;
  exec sleep infinity'

Or in an entrypoint script:

grep -v "^::1.*localhost" /etc/hosts > /tmp/hosts.fix \
  && cat /tmp/hosts.fix > /etc/hosts && rm /tmp/hosts.fix || true

Related Issues

  • #14528 — Same symptom (login fails in devcontainers), closed without root cause identified. Workaround was mounting host ~/.claude into the container.
  • #20793 — Same symptom, closed as duplicate of #14528. Proposed device code flow as alternative.

Claude Code Version

Latest (installed via https://claude.ai/install.sh)

Platform

Anthropic Console (OAuth)

Operating System

Linux (Docker containers on Linux host; likely affects macOS Docker Desktop too)

Terminal/Shell

VS Code integrated terminal + VS Code sidebar (Claude Code extension)

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗