The problem was originally reported in https://github.com/bitcoin/bitcoin/issues/22450#issuecomment-880602891. This is a dedicated issue to track it independently because it is a different problem than the one reported in #22450 OP/description.

> If the same I2P address appears in multiple new buckets, then the first time this loop finds that address, it'll update the port and reposition it in that bucket. Any subsequent times that the address is encountered, it will already have had its port updated so the inner loop will `continue` ...

Thanks, @jnewbery!
{
    
    CAddrManTest addrman;
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    const CAddress new1{ResolveService("5.5.5.5"), NODE_NONE};
    const CAddress new2{ResolveService("6.6.6.6"), NODE_NONE};
    const CAddress tried1{ResolveService("7.7.7.7"), NODE_NONE};
    const CAddress tried2{ResolveService("8.8.8.8"), NODE_NONE};
    addrman.Add({new1, tried1, new2, tried2}, CNetAddr{});
    addrman.Good(tried1);
    addrman.Good(tried2);
    BOOST_REQUIRE_EQUAL(addrman.size(), 4);
    stream << addrman;
    const std::string str{stream.str()};
    size_t pos;
    const char new2_raw[]{6, 6, 6, 6};
    const uint8_t new2_raw_replacement[]{0, 0, 0, 0}; 
    pos = str.find(new2_raw, 0, sizeof(new2_raw));
    BOOST_REQUIRE(pos != std::string::npos);
    BOOST_REQUIRE(pos + sizeof(new2_raw_replacement) <= stream.size());
    memcpy(stream.data() + pos, new2_raw_replacement, sizeof(new2_raw_replacement));
    const char tried2_raw[]{8, 8, 8, 8};
    const uint8_t tried2_raw_replacement[]{255, 255, 255, 255}; 
    pos = str.find(tried2_raw, 0, sizeof(tried2_raw));
    BOOST_REQUIRE(pos != std::string::npos);
    BOOST_REQUIRE(pos + sizeof(tried2_raw_replacement) <= stream.size());