Reverted with reason string 'TimelockController: underlying transaction reverted'

Hello guys .
i am using openzepellin Govenance Wizard contract & openzepellin TimelockController .
i am getting this error
execution reverted: TimelockController: underlying transaction reverted" when i try to execute.
This is all in hardhat test file. When i comment the executeTx there is no error. So this is reverting with execute func.
I also try with do scripts with testing on localhost. it is working there.
I saw this but looks like hasnt solved by anyone. Execution reverted: TimelockController: underlying transaction reverted"

This is function i try to call with that execute func.

function apporoveFundingByDao(
        string memory _ipfsHash,
        uint256 _fundingGoalAmount,
        uint256 _time,
        address _projectOwnerAddress
    )

:1234: Code to reproduce

// FUNC_FUND="apporoveFundingByDao"
projectOwner = (await ethers.getSigners())[4];
governor = await ethers.getContract("GovernerContract");

 const letsTry = await ethers.getContract(
            "GovernanceToken",
            projectOwner
          );
          const args = "QmPwX1rNoYRmQAPDm8Dp7YSeFdxPKaczWaBu8NPgVpKufu";
          const encodedFunctionCall = governor.interface.encodeFunctionData(
            FUNC_FUND,
            [args, s_fundRaisingGoalAmount, s_fundingTime, projectOwner.address]
          );
const proposalTx = await governor.propose(
            [governor.address],
            [0],
            [encodedFunctionCall],
            args
          );
          const proposeReceipt = await proposalTx.wait(1);

After successfully voting proposalState returns 4,

 // its time to queue & execute
          governor = await ethers.getContract("GovernerContract");
          const descriptionHash = ethers.utils.keccak256(
            ethers.utils.toUtf8Bytes(args)
          );

          console.log("Queueing...");
          const queueTx = await governor.queue(
            [governor.address],
            [0],
            [encodedFunctionCall],
            descriptionHash
          );
          await queueTx.wait(1);
          await moveTime(MIN_DELAY + 1);
          await moveBlocks(1);
          console.log("Executing...");
          const executeTx = await governor.execute(
            [governor.address],
            [0],
            [encodedFunctionCall],
            descriptionHash
          );
          await executeTx.wait(1);

:computer: Environment

When i change the projectOwner to deployer its executing but it doesnt make sence.

deployer = (await getNamedAccounts()).deployer;

  const encodedFunctionCall = governor.interface.encodeFunctionData(
          FUNC_FUND,
          [args, s_fundRaisingGoalAmount, s_fundingTime, deployer]
        );

Last update, changing to deployer was work on localhost but it didnt work on testnet. So looks like when you put some variable other then uint this function (governor.interface.encodeFunctionData) or openzeppelin contract doesnt like it. So i change address with uint address index. map that index to address. Solved the problem.

Maybe you can paste the full code of your function apporoveFundingByDao()

function apporoveFundingByDao(
    string memory _ipfsHash,
    uint256 _fundingGoalAmount,
    uint256 _time,
    uint256 _projectOwnerAddressIndex
) external onlyOwner {
    // only dao can call it
    if (
        !_isEnteranceFeePaid[
            projectOwnerAddressIndex[_projectOwnerAddressIndex]
        ]
    ) {
        revert FundProject__EnteranceFeeNeeded();
    } else {
        projectToTime[projectId][_time] = block.timestamp;
        _ProjectFundingStatus[projectId] = ProjectFundingStatus.ONPROGRESS;
        time[projectId] = _time;
        projectFundingGoalAmount[projectId] = _fundingGoalAmount;
        hashToProjectId[_ipfsHash] = projectId;
        idToHash[projectId] = _ipfsHash;
        projectOwnerAddress[projectId] = projectOwnerAddressIndex[
            _projectOwnerAddressIndex
        ];
        _isApporovedByDao[projectId] = true;
        _isFunding[projectId] = true;
        emit projectGoesToFunding(projectId);
        projectId++;
    }
}

Seems like only one revert message in your function, so maybe you can have a check for the requirement

_isEnteranceFeePaid[
    projectOwnerAddressIndex[_projectOwnerAddressIndex]
]