Common errors when interacting with NebGov on Stellar, along with their causes and fixes.
- RPC Errors
- Wallet Errors
- Transaction Errors
- Contract Errors
- Voting and Proposals
- Liquidity Contract Errors
- FAQ
Cause: The contract invocation failed during simulation. Common reasons: wrong function name, incorrect argument types, or the contract is not deployed at the configured address.
Fix:
- Verify the function name matches the contract ABI exactly (case-sensitive).
- Check that all argument types are correct (e.g.,
u32vsi128,AddressvsString). - Confirm
NEXT_PUBLIC_GOVERNOR_ADDRESSin your.envpoints to a deployed contract on the correct network. - Run the simulation manually with
stellar contract invoke --simulate-onlyto inspect the full error.
Cause: The app cannot reach the Stellar RPC endpoint. The NEXT_PUBLIC_RPC_URL environment variable is missing or points to the wrong host.
Fix:
- Open your
.env(or.env.local) and verifyNEXT_PUBLIC_RPC_URL.- Testnet:
https://soroban-testnet.stellar.org - Futurenet:
https://rpc-futurenet.stellar.org - Mainnet:
https://soroban-mainnet.stellar.org
- Testnet:
- Restart the dev server after changing
.env. - Check that your firewall or VPN is not blocking outbound HTTPS to port 443.
Cause: The wallet is connected to a different Stellar network than the one the app targets.
Fix:
- In your wallet (Freighter, Lobstr, etc.), switch to the network specified in
NEXT_PUBLIC_NETWORK_PASSPHRASE. - The passphrase values are:
- Testnet:
Test SDF Network ; September 2015 - Mainnet:
Public Global Stellar Network ; September 2015
- Testnet:
Cause: WalletKitProvider is not wrapping the component tree, or the provider was initialized after the component mounted.
Fix:
- Ensure
<WalletKitProvider>wraps your root layout inapp/layout.tsx(or equivalent). - Do not conditionally render the provider — it must wrap the tree unconditionally.
- If using
useWalletKit(), confirm you are inside a component that is a descendant of the provider.
Cause: The user cancelled the signing prompt in their wallet extension.
Fix: This is expected behavior. Show a non-blocking notification and allow the user to retry. Do not treat this as an application error.
Cause: The wallet returned an empty or malformed public key. This can happen when the wallet is locked or has not yet granted permission to the dApp.
Fix:
- Unlock your wallet and approve the site connection request.
- Disconnect and reconnect via the Connect Wallet button.
- If the issue persists, clear wallet permissions for the site and reconnect.
Cause: The submitted fee is too low for current network conditions. The Stellar network uses a fee market during congestion.
Fix:
- Use fee bumping: submit the transaction with a higher
base_fee(e.g., 1 000 000 stroops during high congestion). - The SDK's
submitTransactionhelper retries with fee bumping automatically. Ensure you are on SDK v0.3.0+. - Monitor current base fees with
stellar ledger fetch --network testnet | jq .base_fee.
Cause: The transaction's timeBounds window elapsed before it was included in a ledger. This often happens when the user delays signing.
Fix:
- Increase
timeboundsSecondsin the transaction builder (default is 30 s; try 120 s for complex multi-step flows). - Prompt the user to sign promptly after building the transaction.
Cause: A persistent storage entry (e.g., a proposal or LP position) does not exist at the queried key. The contract may be on a different network than expected.
Fix:
- Confirm
NEXT_PUBLIC_GOVERNOR_ADDRESSandNEXT_PUBLIC_TIMELOCK_ADDRESSare set for the correct network. - Verify the data was written in a transaction that was successfully included in the ledger (not just simulated).
Cause: The proposal ID does not exist in the governor contract. Either the wrong contract address is configured, or the proposal was never created on this network.
Fix:
- Verify
NEXT_PUBLIC_GOVERNOR_ADDRESSmatches the deployed governor on the active network. - Confirm the proposal was created by checking the transaction that called
propose().
Cause: The caller's signature was not present in the transaction authorization envelope, or the wrong address was passed as the caller argument.
Fix:
- Ensure the wallet's public key matches the address passed to privileged functions (e.g.,
governor,admin,provider). - When constructing the transaction, call
addSignatureBase64or equivalent for each required signer before submitting.
Cause: No liquidity pool exists for the given outcome pair, or the pair was registered under a different token ordering.
Fix:
- Confirm
create_poolandinitialize_poolwere called by the governor for this outcome pair. - Outcome pair ordering is canonical (smaller id first) — querying
(1, 2)and(2, 1)resolve to the same pool. - Check that the
NEXT_PUBLIC_LIQUIDITY_ADDRESSenv var points to the correct contract.
Cause: cast_vote was called before the proposal's start_ledger. The proposal is in Pending state.
Fix:
- Call
governor.state(proposal_id)to get the current state. - Read
proposal.start_ledgerand wait until the current ledger sequence surpasses it. - The UI should disable the Vote button until the proposal is
Active.
Cause: The wallet address has already cast a vote for this proposal. The contract rejects duplicate votes.
Fix: Each address may vote only once per proposal. Show the user their existing vote choice instead of the voting form.
Cause: The user has no delegated voting power at the proposal's snapshot ledger. Tokens must be delegated (self-delegated or to another account) before the proposal was created.
Fix:
- Call
votes.delegate(account, account)to self-delegate before the next proposal is created. - Wrapping tokens via
token_votes.wrap(amount)and then delegating grants voting power. - Voting power is snapshotted at
proposal.start_ledger - 1; delegation after that point does not count for this proposal.
Cause: cast_vote was called when the proposal is in Succeeded, Defeated, Queued, Executed, or Expired state.
Fix: Check governor.state(proposal_id) before casting a vote. Only Active proposals accept new votes.
Cause: A subsequent deposit to a pool provided a amount_b that is less than the proportional amount required by the current reserve ratio. This prevents price manipulation.
Fix:
- Fetch the current pool reserves with
liquidity.get_pool(outcome_a, outcome_b). - Compute
required_b = amount_a * reserve_b / reserve_abefore callingadd_liquidity. - Pass
required_b(or a value greater than it) asamount_b.
Cause: The deposit amount is below MIN_LIQUIDITY (1 000 units). Tiny deposits are rejected to prevent dust griefing.
Fix: Ensure both amount_a and the computed required_b are at least 1 000 units.
Cause: The swap output amount_out after fees is less than the caller's min_amount_out. The pool moved between simulation and execution.
Fix:
- Re-simulate the swap immediately before submitting to get the latest output.
- Add a slippage tolerance (e.g., 0.5%) to
min_amount_out:min_amount_out = simulated_out * 995 / 1000.
Q: How do I find the correct contract addresses for my network?
A: Contract addresses are published in scripts/ after each deployment. You can also query the factory: liquidity.governor() returns the governor address linked to the liquidity contract.
Q: My transaction simulates successfully but fails on-chain. Why?
A: State can change between simulation and submission (another transaction was included first). Retry the simulation immediately before resubmitting, or add a short polling loop.
Q: How do I run contract calls without the frontend?
A: Use the Stellar CLI:
stellar contract invoke \
--network testnet \
--id <CONTRACT_ADDRESS> \
--source <SECRET_KEY> \
-- <function_name> --arg1 value1Q: Can I test on Futurenet before Testnet?
A: Yes. Set NEXT_PUBLIC_RPC_URL to https://rpc-futurenet.stellar.org and NEXT_PUBLIC_NETWORK_PASSPHRASE to the Futurenet passphrase. Deploy contracts to Futurenet first using stellar contract deploy --network futurenet.
Q: How do I reset local state during development?
A: Re-initialize the contracts with fresh deployments:
stellar contract deploy --wasm target/wasm32v1-none/release/sorogov_governor.wasm --network testnet --source <KEY>Update the address in .env.local and restart the dev server.
Q: Why does get_lp_position return 0 even after adding liquidity?
A: The provider address queried must exactly match the one used in add_liquidity. Also verify that outcome_a and outcome_b are in the correct order (use canonical order: smaller id first, e.g., (0, 1) not (1, 0)).