{"record":{"id":"45118948123c5100","repo":"ipfs/kubo","slug":"bootstrap-address-without-a-transport-s","errorCode":null,"errorMessage":"bootstrap address without a transport: %s","messagePattern":"bootstrap address without a transport: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/commands/bootstrap.go","lineNumber":284,"sourceCode":"\treturn nil\n}\n\nfunc bootstrapAdd(r repo.Repo, cfg *config.Config, peers []string) ([]string, error) {\n\t// Validate peers - skip validation for \"auto\" placeholder\n\tfor _, p := range peers {\n\t\tif p == config.AutoPlaceholder {\n\t\t\tcontinue // Skip validation for \"auto\" placeholder\n\t\t}\n\t\tm, err := ma.NewMultiaddr(p)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttpt, p2ppart := ma.SplitLast(m)\n\t\tif p2ppart == nil || p2ppart.Protocol().Code != ma.P_P2P {\n\t\t\treturn nil, fmt.Errorf(\"invalid bootstrap address: %s\", p)\n\t\t}\n\t\tif tpt == nil {\n\t\t\treturn nil, fmt.Errorf(\"bootstrap address without a transport: %s\", p)\n\t\t}\n\t}\n\n\taddedMap := map[string]struct{}{}\n\taddedList := make([]string, 0, len(peers))\n\n\t// re-add cfg bootstrap peers to rm dupes\n\tbpeers := cfg.Bootstrap\n\tcfg.Bootstrap = nil\n\n\t// add new peers\n\tfor _, s := range peers {\n\t\tif _, found := addedMap[s]; found {\n\t\t\tcontinue\n\t\t}\n\n\t\tcfg.Bootstrap = append(cfg.Bootstrap, s)\n\t\taddedList = append(addedList, s)","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/core/commands/bootstrap.go#L266-L302","documentation":"After confirming the multiaddr ends in a `/p2p` component, bootstrapAdd also checks that a transport part exists (`tpt == nil`). If SplitLeft consumed everything so no transport remains, the peer entry lacks a transport address and the command returns `bootstrap address without a transport: %s`.","triggerScenarios":"`ipfs bootstrap add` with an address consisting solely of the p2p component where a transport-bearing multiaddr is required by this code path, so SplitLast yields tpt==nil.","commonSituations":"Scripts that pass bare `/p2p/<peerID>` peer IDs where a full transport address is expected; address-building code that strips the transport when cleaning input.","solutions":["Provide a full address with transport and peer ID: `/ip4/<ip>/tcp/<port>/p2p/<peerID>` or `/dnsaddr/<host>/p2p/<peerID>`","Fetch the correct full address from `ipfs id` on the peer","Check for a code/script bug that drops the transport portion of the multiaddr"],"exampleFix":"// before\nipfs bootstrap add /p2p/QmaCpDMG...\n// after\nipfs bootstrap add /dnsaddr/bootstrap.libp2p.io/p2p/QmaCpDMG...","handlingStrategy":"validation","validationCode":"func hasTransport(p string) bool {\n    m, err := ma.NewMultiaddr(p)\n    if err != nil { return false }\n    tpt, last := ma.SplitLast(m)\n    return last != nil && last.Protocol().Code == ma.P_P2P && tpt != nil\n}","typeGuard":"null","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"without a transport\") {\n    // replace bare /p2p/<peerID> with a full transport address\n}","preventionTips":["Use /dnsaddr/<host>/p2p/<peerID> or /ip4/.../tcp/.../p2p/<peerID> forms","Do not strip transport components when sanitizing addresses","Verify with SplitLast that a transport remains alongside the p2p part","Source addresses from the `ipfs id` Addresses field"],"tags":["cli","bootstrap","multiaddr","validation"],"backgroundTag":"invalid-multiaddr","analyzedSha":"329838acdfafae224582930457efe80aa217afc0","analyzedAt":"2026-09-03T18:30:52.135Z","contentChangedAt":"2026-09-03T18:30:52.135Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}