canopy-network/canopy · error

invalid dial peer %s: %s

Error message

invalid dial peer %s: %s

What it means

When dialing statically configured peers, the node parses each configured peer string into a lib.PeerAddress via FromString. This error wraps any parse failure, identifying both the offending address string and the underlying reason (bad format, network/chain mismatch, etc.).

Source

Thrown at p2p/p2p.go:195

			if err = p.AddPeer(c, &lib.PeerInfo{Address: &lib.PeerAddress{NetAddress: netAddress}}, false, false); err != nil {
				p.log.Error(err.Error())
				_ = c.Close()
				return
			}
		}(c)
	}
}

// DialForOutboundPeers() uses the config and peer book to try to max out the outbound peer connections
func (p *P2P) DialForOutboundPeers() {
	// create a tracking variable to ensure not 'over dialing'
	var dialing atomic.Int32
	getPeerFromString := func(address string) (*lib.PeerAddress, error) {
		// start a peer address structure using the basic configurations
		peer := &lib.PeerAddress{PeerMeta: &lib.PeerMeta{NetworkId: p.meta.NetworkId, ChainId: p.meta.ChainId}}
		// try to populate the peer address using the peer string from the given string
		if err := peer.FromString(address); err != nil {
			return nil, fmt.Errorf("invalid dial peer %s: %s", address, err.Error())
		}
		// exit
		return peer, nil
	}
	// Try to connect to the DialPeers in the config
	for _, peerString := range p.config.DialPeers {
		peerAddress, err := getPeerFromString(peerString)
		if err != nil {
			// log the invalid format
			p.log.Error(err.Error())
			// continue with the next
			continue
		}
		// dial in a non-blocking fashion
		go func() {
			// increment dialing
			dialing.Add(1)
			// dial the peer with exponential backoff

View on GitHub (pinned to ee8197d91d)

Solutions

  1. Check the wrapped inner error to see which part of the address failed (format vs network/chain ID)
  2. Correct the peer address string in the DialPeers config to match the expected format and the node's NetworkId/ChainId
  3. Obtain a fresh peer address string from the target peer's published address or another node on the same network
  4. Remove stale entries from DialPeers if the peer is no longer available

Example fix

// before (config)
"dial_peers": ["/ip4/10.0.0.1/tcp{PORT}/p2p/BADID"]
// after
"dial_peers": ["/ip4/10.0.0.1/tcp5001/p2p/12D3KooWCorrectBase58ID"]
Defensive patterns

Strategy: validation

Validate before calling

if peerString == "" { return errors.New("empty dial peer") }
probe := &lib.PeerAddress{PeerMeta: &lib.PeerMeta{NetworkId: expectedNet, ChainId: expectedChain}}
if err := probe.FromString(peerString); err != nil {
    return fmt.Errorf("config dial_peers entry %q invalid: %v", peerString, err)
}

Type guard

func isValidPeerAddress(s string, net, chain string) bool {
    p := &lib.PeerAddress{PeerMeta: &lib.PeerMeta{NetworkId: net, ChainId: chain}}
    return p.FromString(s) == nil
}

Try / catch

peer, err := getPeerFromString(addr)
if err != nil {
    log.Warnf("skipping bad dial peer: %v", err)
    continue // or fail fast on startup, depending on policy
}

Prevention

When it happens

Trigger: Config field DialPeers contains a string that PeerAddress.FromString cannot parse — malformed multiaddr/peer string, wrong network ID or chain ID in the address, or empty/garbage entry in the config list.

Common situations: Typo in a dial peer address in config.json, copying a peer address from a different network (mainnet address on testnet config), stale peer strings after a protocol upgrade changed the address format.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of canopy-network/canopy@ee8197d91d (2026-09-06). Data as JSON: /api/errors/39131ec99526707f. Report an issue: GitHub.