Troubleshooting
Something not working? Find the message you see below. Each entry covers what it means, how to check, and how to fix it.
- Tipping or maintaining a repo on opentip.tech? Start with Tipping a repo.
- Running your own Opentip instance? Go to Running your own instance.
Still stuck? Email support@opentip.tech (see Contact). Include the repo (owner/repo), your wallet address, and the exact message you see.
For people tipping and maintaining repos
Tipping a repo
"Wrong network"
You see "Switch your wallet to Base before sending." or "That transaction was not confirmed on Base."
- Why: Your external wallet is on a different network. Opentip asks your wallet to switch before it sends. You see this if the switch was declined or failed.
- Fix: Switch your wallet to Base yourself, then press Tip again. If the second message appeared, check your wallet's activity to see which network the transaction went to before you try again.
"ETH price is unavailable. Try again shortly."
- Why: Opentip checks that an ETH amount is worth at least $1, and it needs a live ETH price for that. If the price can't be loaded, ETH tipping is paused rather than letting the $1 minimum slip.
- Fix: Wait a minute and try again, or tip in USDC for now.
"Minimum tip is $1"
- Why: The minimum is $1. USDC counts as $1 per token. ETH uses the live price.
- Fix: Enter a larger amount. You may also see "Enter an amount" (empty or zero) or "Invalid amount" (too many decimal places for the token).
"OAR tipping coming soon"
- Why: Choosing OAR in the token menu shows this message and keeps your current token. OAR is coming soon, so it isn't available in the tip form yet.
- Fix: Tip in USDC or ETH.
"Connect wallet"
- Why: You pressed Tip without a connected wallet.
- Fix: Press Connect wallet next to the amount, pick your wallet, then try again. You don't need an Opentip account to tip. See Getting Started.
Not enough balance
You see "Transaction rejected" or "Tip failed", followed by your wallet's own error.
- Why: Opentip doesn't check your balance before it sends. Your wallet or the network refuses the transaction instead.
- Check: Make sure you hold the amount on Base, plus a little ETH for gas.
- Fix: Top up on Base and try again. Tokens sent on other networks won't show up.
You cancelled in your wallet
- ETH: You see "Transaction rejected". Nothing was sent. Try again whenever you're ready.
- USDC approve, USDC payment, claim, or register from an external wallet: Cancelling the prompt shows "Transaction rejected" and the button leaves "Approving spend..." or "Sending...". A failed approval shows "Approve failed". A failed payment shows "Tip failed". A failed claim shows "Claim failed". A failed registration shows "Register failed". USDC from an external wallet still needs two confirmations when a new approval is required: the exact amount, then the payment.
- Opentip Smart Wallet: You see "Tip failed" with the reason.
Opentip Smart Wallet errors
| You see | What to do |
|---|---|
| "Couldn't reach Coinbase CDP — an adblocker or privacy extension is likely blocking it. …" | Turn off ad blockers or privacy extensions for this site, or try a private window with no extensions. |
| "CDP session not ready — try again" | Try again. If it keeps happening, sign out and back in. |
| "No Opentip Smart Wallet — create one in /tip/dashboard/wallets first" | Create your wallet at Dashboard → Wallets. |
| "Blocked by wallet security policy." or a limit or recipient message | Check your controls at Wallet → Security. |
| "… (you paid gas. Daily sponsorship is used up)" after a successful payment | Your payment went through. Today's sponsored transactions (10 per account, reset at midnight UTC) were used up, so the wallet paid its own gas. Sponsorship only runs when a paymaster is configured. |
| "… (you paid gas)" after a successful payment | The paymaster was unavailable, so the wallet paid its own gas. This happens on 502 and 503 from the paymaster. |
For devices, sessions, and more Smart Wallet help, see Smart Wallet.
Your payment went through but doesn't appear in "Recent tips" or "Top supporters"
- Why: The Pending, Total tipped, and Tips numbers on a repo page come straight from the contract, so they update right away. A repo with no tips shows an em dash for an empty total. Recent tips, Top supporters, the leaderboard, and the totals on Repos come from our indexer. The indexer reads the chain on a timer and can fall behind.
- Fix: Give it a few minutes and reload. If the contract numbers changed, your tip arrived.
Display name
- Why: Saving a name needs a signature from the wallet you tip with. That works for a connected external wallet and for the Opentip Smart Wallet. The name is stored for that wallet's address.
- Fix: Type a name of 1–64 characters, press Save, and sign the message.
- "Enter a display name" means the name is empty, or no wallet address is available yet.
- "Save failed" with "invalid signature" means the signature didn't match that address.
Accounts and sign-in
"Invalid email or password" / "Login failed"
- Why: The email or password is wrong, or the account was created with GitHub and has no password yet.
- Fix: Use Forgot your password?, or press Continue with GitHub. To add a password to a GitHub account, open Dashboard → Account and use Set password.
Errors when creating an account
- "Email already registered": Sign in instead, or reset your password.
- "Password must be 8-128 characters" or "Password must be at least 8 characters": Choose a longer password.
- "rate limit exceeded, retry after Ns": Wait that many seconds and try again. Signed-out attempts are limited by client IP.
- "Account created, but the verification email failed to send. Sign in and resend the code.": The account exists. Sign in and request a new code. The page also takes you to email verification so you can resend.
Email verification code problems
| You see | What to do |
|---|---|
| "invalid or expired code" | Codes expire after 1 hour. Press Didn't get it? Resend and use the newest code. |
| "too many attempts, request a new code" | Request a new code, then enter it carefully. |
| "wait N seconds before requesting another code" | You can request a new code once a minute. |
| "email already in use" | That address belongs to another Opentip account. Sign in to that account instead. |
| "email already verified and cannot be changed" | Your email is already verified. Nothing to do. |
| "verification email failed to send" | The resend did not go out. Check that you can try again, then press Didn't get it? Resend. |
| No email arrives | Check spam, wait a minute, then press Didn't get it? Resend. If the button reports that the email failed, the message was not sent. |
Connecting GitHub from Dashboard → Account fails
| You see | What to do |
|---|---|
| "Invalid OAuth state" | The GitHub step took more than 10 minutes, or it finished in a different browser. Press Connect GitHub again and finish in the same browser. |
| "GitHub OAuth failed" / "Failed to fetch GitHub info" | Try again. If you denied access on GitHub, approve it this time. |
| "This GitHub account is already linked to another user" | That GitHub account is linked to a different Opentip account. Sign in with Continue with GitHub to reach it. |
"Continue with GitHub" brings you back to the sign-in page
- Likely cause: You already have an email account that uses the same address as your GitHub account.
- Fix: Sign in with email, then open Dashboard → Account and press Connect GitHub.
"Wallet creation failed" during setup
Fix: Press Try again. If the message mentions Coinbase or a blocked request, turn off ad blockers for this site first. "smart wallet already exists for this account" means you already have one. Find it in Dashboard → Wallets.
Registering a repo (maintainers)
Background: For Developers walks through registration step by step.
"Can't register: not repo owner, maintainer, or collaborator" on an org repo
The toast title is "Can't register". The page also shows "Not owner. Ensure you have admin/write on GitHub."
- Why: For a repo owned by an organization, Opentip asks GitHub for your permission level using your own GitHub sign-in. GitHub must report admin, maintain, or write. The most likely cause is that the organization restricts third-party OAuth apps and hasn't approved Opentip. GitHub then won't answer for that org's repos.
- Check: You're signed in with GitHub, or you connected GitHub in Dashboard → Account. An email-only sign-in isn't enough. Your role on the repo is write, maintain, or admin. Triage and read aren't enough.
- Go to github.com/settings/applications, open Authorized OAuth Apps, and choose Opentip.
- Under Organization access, press Grant for the org (org owners) or Request (members).
- If you requested access, an org owner approves it under the org's Settings → Third-party access → OAuth application policy.
- Back on Opentip, press Re-check. If it still fails, sign out and sign back in with GitHub.
"Can't register" while you're signed in with email
- Why: Registration needs a GitHub identity. The description is "Connect GitHub to verify ownership". A request with no session still says "not authenticated".
- Fix: Connect GitHub in Dashboard → Account, then press Verify ownership again.
"Can't register" with "repo not found"
- Why: GitHub didn't return the repo. It may be private, or the URL may have a typo. This is not a rate limit.
- Fix: Check that the page URL matches
opentip.tech/tip/owner/repoexactly and the repo is public. Then try again.
"Can't register" with a rate-limit message
- "GitHub rate limit exceeded, retry after Ns": GitHub limited the lookup. Wait the number of seconds shown, then press Re-check.
- "rate limit exceeded, retry after Ns": Opentip's own ownership check allows 3 per minute. Wait, then press Re-check.
Payout address errors
| You see | What it means | Fix |
|---|---|---|
| "Could not verify that this payout address can receive ETH. Try again." | Opentip couldn't reach the network to check your payout address. | Try again in a moment. |
| "This contract cannot receive ETH. claimAll reverts the whole claim if the ETH transfer fails, which also blocks token withdrawals." | Your payout address is a contract that refuses ETH. Using it would lock all your payouts. | Choose a different payout wallet, such as your Opentip Smart Wallet or a regular wallet address. |
"You need to link a wallet first. This wallet receives tips."
Fix: Press Link wallet → and create your Opentip Smart Wallet or link an external wallet. This wallet receives your tips. See Smart Wallet.
"Register failed"
- Why: Your ownership check is valid for 5 minutes. If you waited longer before you pressed Register repo, the contract rejects it. A wallet on the wrong network also causes this. Cancelling the wallet prompt shows "Transaction rejected" instead, and the button recovers.
- Fix: Reload the page, press Verify ownership again, then press Register repo right away. Make sure your wallet is on Base.
Your repo moved from a personal account into an org
- Why: Opentip registers repos by their
owner/repopath. After a move, the repo has a new path, and that path is registered separately. - Fix: Open the new path and register it there. The new owner is an org, so the org-repo entry above applies. Share the new link. Tips already sent to the old path stay under the old path. The payout wallet for the old path can still claim them on the old page.
Repo page says "Could not fetch GitHub metadata."
- Why: Opentip couldn't load the repo's details from GitHub. Tipping, registering, and claiming still work on that page.
- Fix: Reload in a few minutes. If it continues on opentip.tech, email support@opentip.tech with the repo name.
A repo page shows "404"
- Why: The repo has been hidden by an Opentip admin. Hidden repos show the not-found page and don't appear on Repos.
- Fix: If it's your repo, email support@opentip.tech with the repo name.
Dashboard → GitHub repos is empty or missing repos
| You see | What to do |
|---|---|
| "Connect GitHub to list your repositories" | Connect GitHub in Dashboard → Account. |
| "No repos found." or an org repo is missing | This list includes repos you own, repos you collaborate on, and org repos you reach as an organization member, including through a team. An org that hasn't approved Opentip can still hide its repos. Open the repo page directly in that case. |
| "Your GitHub connection expired. Sign out, then sign in with Continue with GitHub." | GitHub rejected the saved token. Sign out, then sign in with Continue with GitHub. |
| "Couldn't load your GitHub repositories. Try again in a minute." | GitHub returned another error. The page shows this message instead of raw GitHub JSON. |
The repo icon doesn't change after you pick an image
- Check: The image is JPEG, PNG, WebP, or GIF and 2 MB or smaller.
- Why else: Icons can be changed only for repos whose payout wallet is linked to your account. Those are the repos listed in Dashboard → Repos.
- Fix: The page shows the error. "file too large (max 2MB)", "invalid file type", "not authorized", "repo not found", and "upload failed" are the messages to look for. Pick a smaller image and try again, or email support@opentip.tech if it still doesn't change.
Claiming tips (maintainers)
There's no "Claim all" button
- Why: The button shows only when two things are true. The wallet you're using must be the repo's payout wallet, and the repo must have a pending balance.
- Check: Compare the Payout address on the repo page with your wallet. Pending must show an amount.
- Fix: Connect the payout wallet, or sign in so your Opentip Smart Wallet is used if it's the payout. In Dashboard → Repos, Claim takes you to the repo page. Lost the payout wallet? See the recovery note in For Developers.
"Claim failed"
- Check: Your wallet is on Base and has a little ETH for gas.
- Fix: Try again from the repo page. Cancelling the wallet prompt shows "Transaction rejected" and the button leaves "Sending...". If it keeps failing, email support@opentip.tech with the repo name and payout address.
Notifications
Push notifications never arrive
- Open Dashboard → Notifications.
- If you see "Install Opentip as a PWA to receive push notifications.", install it from /tip/install and open the installed app.
- Press Enable Notifications and allow them in your browser.
- Press Subscribe. The page says "Subscribed on this device." when it worked. Each device you use needs its own subscription. Pushes go to all of them.
- A new subscription starts with Tip received, Tips paid out, Tip submitted, and Claim submitted checked. Subscribing again, or on another device, keeps the saved choices. Clear every box and pushes stop: the page says "No types selected, so pushes are not sent." Saving types before you subscribe shows "Subscribe before saving notification types." A successful save says "Notification types saved."
- Press Send Test. The page says "Test notification sent." "not subscribed" means this account has no device yet. "push failed" or "Test notification failed" means the send did not complete.
- "Service workers not supported" means this browser can't receive push. Try a different browser.
- "VAPID key not configured" means this Opentip instance has no public push key.
- "This browser can't receive push notifications here. Install Opentip as a PWA or try another browser." is what you see when the browser has no Notification API, including iOS Safari outside a PWA. The page stays up.
- "Subscribe failed" means the browser or the server refused the subscription. Denying the permission shows the browser's own message.
Someone tipped your repo but you got no notification
- Check: The repo's payout wallet must be linked to your Opentip account. Tip notices go to the account that owns that wallet.
- Check: A new subscription includes tip received and tips paid out until you turn them off. After you clear every type, the page says "No types selected, so pushes are not sent." and nothing is delivered.
- Check: Look at your in-app list at /tip/notifications. Notices show up there even if push didn't reach you. They can take a few minutes, because they come from the indexer.
Stats and leaderboard look wrong or stale
- Live from the contract: Pending, Total tipped, Tips, and Payout on a repo page, plus pending and total amounts in Dashboard → Repos. An empty total is an em dash, not a row of zeros.
- From the indexer, can lag a few minutes: Recent tips, Top supporters, the leaderboard, totals and sorting on Repos, and tip counts in the dashboard.
- Most tipped on Repos sorts by USD value, using each token's decimals. When the ETH price is unavailable, ETH amounts count as $0 for that sort.
- Dollar values use current prices, so rankings can shift as prices move.
- "Couldn't load the leaderboard. Try again in a minute." means the leaderboard query failed. "No tips yet." on that page means it loaded and the list is empty. A repo page can still say "No tips yet." when that repo has no supporters.
- One person can appear twice. The leaderboard is grouped by wallet address, so payments from a Smart Wallet and an external wallet show as separate supporters.
Running your own instance
These entries are for operators and self-hosters. For setup and the full list of env vars, see Contributing.
Repo pages show "Could not fetch GitHub metadata."
- Why:
fetchRepoMeta(frontend/lib/github.ts) returns nothing on any non-OK GitHub response, and the page shows this message. It sendsGITHUB_TOKENwhen one is set.
Call GitHub with the same token and read the status and message:
curl -i -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/repos/OWNER/REPO| Status | Meaning |
|---|---|
| 200 OK | The token works for this repo. |
| 401 "Bad credentials" | The token is wrong or truncated, and every repo fails. |
| 403 | An org policy or a rate limit. Read the message. |
| 404 | The repo is private or doesn't exist. |
- Fine-grained tokens start with
github_pat_and are about 93 characters long. Check the length withprintf %s "$GITHUB_TOKEN" | wc -c. With no token, calls share GitHub's limit of 60 per hour. - Known case: A fine-grained token with a lifetime over 366 days gets 403 for one org's repos only when that org forbids fine-grained tokens with lifetimes over 366 days. Repos elsewhere still work.
- Fix: Regenerate the token with an expiration within the org's limit, or ask an org owner to check the org's fine-grained token policy and any pending token requests. Update
GITHUB_TOKENand redeploy. GitHub responses are cached for about 5 minutes (revalidate: 300), so give the page that long to recover.
Org repos fail ownership checks
How the check works (frontend/app/api/verify-ownership/route.ts):
- It loads the repo with the user's GitHub token, then
GITHUB_TOKEN. It calls GitHub with no token only when neither is set, or when the answer is not a definitive 404. A GitHub rate limit is "GitHub rate limit exceeded, retry after Ns". A missing repo stays "repo not found". - It compares
repo.owner.loginwith the signed-in GitHub login. This fails for org repos. - It calls
GET /repos/{owner}/{repo}/collaborators/{login}/permission, first with the user's GitHub OAuth token (scoperead:user public_repo), then withGITHUB_TOKEN. - It accepts
admin,maintain,write, orpush.
- Why the user's token matters: A fine-grained
GITHUB_TOKENgets 403 "Resource not accessible by personal access token" on this endpoint for org repos. The user's OAuth token has to succeed, and an org's OAuth app restrictions can block it.
curl -i -H "Authorization: Bearer $GITHUB_TOKEN" \
https://api.github.com/repos/OWNER/REPO/collaborators/LOGIN/permission- Fix: Have the user grant or request org access for your OAuth app. See the org repo entry above.
"Can't register: internal error"
- Why: The route threw an error, most often because
REGISTRAR_PRIVATE_KEYisn't set or the contract address isn't configured. - Check: Look in the server logs for a line that starts with
verify-ownership error:, such asREGISTRAR_PRIVATE_KEY env var not set. - Fix: Set the missing variable and redeploy.
Every registration ends in "Register failed"
- Why: The permit is signed by
REGISTRAR_PRIVATE_KEYfor the chain and contract in your config. It fails on-chain if that key isn't the contract's registrar signer, which is set withsetRegistrarSigner, orNEXT_PUBLIC_CHAINor the contract address points somewhere else. - Check: A successful
/api/verify-ownershipresponse includessigner. Compare it with the contract's registrar signer. - Fix: Use the matching key, or rotate the signer on the contract.
Every payout address fails with "Could not verify that this payout address can receive ETH. Try again."
- Why: The check needs
NEXT_PUBLIC_RPC_URL. If the variable is empty, or the RPC fails, every address fails this way, including plain wallets. - Fix: Set
NEXT_PUBLIC_RPC_URLto a working RPC for your chain and redeploy.
"Contract address not configured." or the wrong network name
- Why:
NEXT_PUBLIC_CHAINmust be exactlybasefor mainnet. Any other value means Base Sepolia. The contract address then comes fromNEXT_PUBLIC_BASE_CONTRACT(mainnet) orNEXT_PUBLIC_BASE_SEPOLIA_CONTRACT(testnet). - Symptom: Users on a mainnet site see "Switch your wallet to Base Sepolia before sending." when the chain setting is wrong.
- Fix: Set both variables to match, then rebuild.
NEXT_PUBLIC_values are baked in at build time.
Lists, leaderboard, and stats are stale or missing
- Why: The indexer (
indexer/src/index.ts) polls the contract and writes tips, registration, payout, and claim events to Postgres. Claim events are stored and can trigger a "Tips paid out" notification. - Check: Look in the indexer logs for
[indexer] tick erroror[indexer] DB unreachable. Look atlast_blockin theIndexerStatetable and compare it with the chain head. CHAINisn'tbaseon mainnet. The indexer readsCHAIN, notNEXT_PUBLIC_CHAIN.RPC_URLorCONTRACT_ADDRESSis wrong. WithoutCONTRACT_ADDRESS, the indexer exits withCONTRACT_ADDRESS env required.GETLOGS_RANGEdefaults to 10 blocks per request, so catching up is slow. Increase it to whatever your RPC plan allows.- There's no checkpoint and no
START_BLOCK, so only the last 10,000 blocks were scanned. SetSTART_BLOCKto the contract's deployment block and reset the checkpoint. POLL_MSdefaults to 12000.
Push notifications don't work on your instance
VAPID_PUBLIC_KEY,VAPID_PRIVATE_KEY, andNEXT_PUBLIC_VAPID_PUBLIC_KEYare set. Users otherwise see "VAPID key not configured", and Send Test fails withVAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY required.NOTIFICATION_SECRETis the same value in the frontend and the indexer. OtherwisePOST /api/notifications/sendreturns 401unauthorized.NOTIFICATION_API_URLin the indexer points to your deployment. It defaults tohttps://opentip.tech/api/notifications/send.- Pushes honor the types saved on each subscription. A new subscription starts with tip_received, claim_available, tip_sent, and claim_submitted. Subscribing again keeps that list, and another device inherits it. An empty list after every type is cleared sends nothing. Every subscription for that user is attempted, and endpoints that return 404 or 410 are removed.
Verification or reset emails never arrive
- Why: If
RESEND_API_KEYis empty, sign-up still creates the account, but the API returns "Account created, but the verification email failed to send. Sign in and resend the code." The server logsFailed to send verification email. Resend returns "verification email failed to send". Password reset returns "failed to send email". - Fix: Set
RESEND_API_KEY, and setRESEND_FROMto a sender your Resend account can use.
Smart Wallet doesn't work on your instance
| Symptom | Check |
|---|---|
| No Smart Wallet option anywhere | NEXT_PUBLIC_CDP_PROJECT_ID is set. Without it, the CDP provider isn't loaded. |
/api/auth/cdp-jwt returns 500 "JWT_PRIVATE_KEY not set — run node scripts/gen-jwt-key.js" | Set JWT_PRIVATE_KEY. Also check JWT_KID, CDP_JWT_ISSUER, and CDP_JWT_AUDIENCE. See the JWKS note in Contributing. |
| "Wallet creation failed" with "wallet verification unavailable — try again" | CDP_API_KEY_ID and CDP_API_KEY_SECRET are set. The server uses them when CdpClient() confirms wallet ownership. |
Smart Wallet payments fail and /api/paymaster returns "paymaster not configured" | Set CDP_PAYMASTER_URL. Keep it server-only. A missing paymaster (500) does not fall back to self-paid gas. 429 and 502, 503 do, and the wallet pays its own gas. |
GitHub sign-in or Connect GitHub fails
- "GitHub OAuth not configured": Set
GITHUB_IDandGITHUB_SECRET. - Callback URLs:
NEXTAUTH_URLmust be your public URL. Connect GitHub sends users back to${NEXTAUTH_URL}/api/account/github/callback. NextAuth sign-in uses its own/api/auth/...callback. Your GitHub OAuth app's callback URL must allow both. - Connecting GitHub again updates the stored access token for that account. Relinking used to keep the old token.
Image uploads fail
- Why: Uploads need
AZURE_STORAGE_ACCOUNTandAZURE_STORAGE_KEY. If they're missing, the upload route returns "upload failed". The repo icon picker shows that error.
ETH tipping is blocked everywhere
- Why: Prices come from CoinGecko's public API and are cached for 60 seconds. If your server can't reach
api.coingecko.com, users see "ETH price is unavailable. Try again shortly." - Fix: Allow outbound access to
api.coingecko.com.
Rate limits act differently across servers
Request limits are kept in each server instance's memory, so they reset on deploy and aren't shared between instances. Ownership checks allow 3 per minute. Sign-up and other writes allow 10 per minute. Signed-out signup is keyed by client IP (x-forwarded-for, then x-real-ip). A session cookie is used when one is present.