NVIDIA / NVIDIA/Personal-AI-Router

[Bug]: pairing fails after a correct PIN when the inviter connected over IPv6 link-local — the joiner's return POST drops the %zone ([fe80::…]:14321 → no route to host)

Open
#69 0 comments 0 reactions 1 assignee View on GitHub

@ckelseynv is already working on this.

Since Sep 18, 2026.

Dominant language
Go
Stars
1.4k
Forks
250
Avg merge
23h 27m
Merged PRs (30d)
1

Description

PAIR version

v0.1.1 (13b6811), all 14 services built from source with Go 1.25.5 (darwin/arm64); same behaviour expected from the release binaries (the code path is services/nvpair-cluster-manager/invite.go).

Operating system

macOS 26.5.2 on both nodes (inviter and joiner).

Hardware

Two Apple Mac mini M4 (16 GB) on one Wi-Fi LAN, IPv4 192.168.86.0/24 plus IPv6 link-local on en0.

Inference engine and model

Ollama 0.30.7 on the joiner (llama3.2:1b); not relevant to the bug.

What happened

cluster:invite-node {"address":"karen.local"} from the inviter returned a PIN and state:"pending"; the joiner received cluster:invite-received and answered cluster:respond-to-invite {inviteId, accept:true, pin} with the correct PIN — and the result was state:"failed". The joiner's log shows why: its completion/notify POST to the inviter used the inviter's IPv6 link-local address without the zone:

[nvpair-cluster-manager] INFO invite inv-9f69cc176d6c: completion failed: Post "http://[fe80::c16:bee4:c6a1:3fd]:14321/v1/cluster/pairing": dial tcp [fe80::c16:bee4:c6a1:3fd]:14321: connect: no route to host
[nvpair-cluster-manager] INFO invite inv-9f69cc176d6c: notify inviter fail attempt 1/3: ... (same, ×3)

fe80::/10 addresses are only routable with a scope (%en0); the inviter had reached the joiner over link-local (the hostname resolved to an IPv6 link-local first), the joiner took the remote address of that inbound connection as the inviter's address, and the zone was dropped when it was rendered back into a URL. Both nodes' /v1/cluster/pairing were reachable over IPv4 the whole time.

Expected

Either keep the zone when rendering a link-local address into the return URL ([fe80::…%25en0]), or prefer a global/IPv4 address from the inviter's advertised ips= list for the return path; at minimum surface the dial error in the respond-to-invite result instead of a bare state:"failed" after a correct PIN.

Steps to reproduce
  1. Two macOS nodes on one LAN with IPv6 link-local enabled (default), PAIR services running on both.
  2. On node A: {"jsonrpc":"2.0","id":1,"method":"cluster:invite-node","params":{"address":"<node-B-hostname>.local"}} — where the name resolves to a link-local IPv6 first (as karen.local did here: fe80::… then 192.168.86.17).
  3. On node B: answer cluster:invite-received with cluster:respond-to-invite and the PIN shown on A.
  4. Observe state:"failed" and the log line above on B.
  5. Repeat with "address":"192.168.86.17" (IPv4 literal): state:"paired", nodes:get-initial shows two members on both sides.
Sanitized logs

Above; nothing else in either log around the failure. Happy to attach the full sanitizer bundle if useful.

Confirmations
  • I searched existing issues (#1 mDNS source port, #3 Local Network permission, #44 multicast drops are adjacent; none covers the pairing return URL).
  • I agree to follow the Code of Conduct.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.