SafeMint fails when using Address.callFunctionWithValue

I'm having a very interesting problem, I have two contracts:

  1. An Openzeppelin ERC721 token with a mint function that utilizes _safeMint
  2. An upgradable marketplace contract (IERC721HolderUpgradeable) that uses AddressUpgradeable (callFunctionWithValue) to call the mint function in #1

If I call the function in #2 that uses callFunctionWithValue to call the mint function in #1, I can trace the transaction to failing at _checkOnERC721Received when it calls IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) at line 395. Then, the tx fails and the token provides no revert message.

If I add a method to #2 and cast the token address like so: ERC721(token_address).mint(...) the transaction succeeds with no issue. This seems like a very niche error in the EVM/compiler, but I am not so sure.

I tried this in solc 0.8.9, 0.8.10 and 0.8.11 with the same results.

Market.sol

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155ReceiverUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";

contract RoolahShopUpgradableV3 is
    IERC165Upgradeable,
    IERC721ReceiverUpgradeable,
    IERC1155ReceiverUpgradeable,
    Initializable,
    OwnableUpgradeable
{
    using AddressUpgradeable for address;
/**
     * Forwards a call to a specified contract address, targeting the given function
     * with the provided abi encoded parameters.
     *
     * This will allow the market to mint directly from projects, etc. (essentially, allows the contract to behave like a wallet)
     *
     * @param contractAt - the address of the target contract
     * @param encodedCall - the ABI encoded function call (use web3.eth.abi.encodeFunctionCall)
     */
    function externalCall(address contractAt, bytes calldata encodedCall)
        external
        payable
        onlyOwner
        returns (bytes memory)
    {
        return contractAt.functionCallWithValue(encodedCall, msg.value);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        returns (bool)
    {
        return
            interfaceId == type(IERC1155ReceiverUpgradeable).interfaceId ||
            interfaceId == type(IERC721ReceiverUpgradeable).interfaceId;
    }

    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(
        address,
        address,
        uint256[] memory,
        uint256[] memory,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155BatchReceived.selector;
    }

    function onERC721Received(
        address,
        address,
        uint256,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC721Received.selector;
    }
}

Token.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

contract MockERC721 is ERC721Enumerable, Ownable {
    /**
     * Public so that we can test
     */
    function safeMint(address user, uint256[] calldata ids) public {
        uint256 i;

        for (i; i < ids.length; i++) {
            _safeMint(user, ids[i]);
        }
    }
}