Tell a connected worker to reconnect, not to connect - #554
Merged
Conversation
Reported from the agenticjobs apply flow: an agent tried to invoice $0.25 and ugig answered "Connect your CoinPay account before sending an invoice". CoinPay was connected. It tried again, and got the same sentence. The gate is right and the sentence is wrong. A CoinPay link authorised before ugig needed wallet:read cannot read global wallet addresses, so it cannot back an invoice, and getConnectedCoinpayAccessToken correctly returns null for it. But null carries no reason, so the route reported the one case it could name: not connected. That is an instruction to do something the worker has already done, which is why it loops. They check, see a connection, and try again. #553 fixed this on the connections page, which now says "Reconnect required" rather than "Connected". The API never learned the difference, and the API is what an agent gets: it never sees that page, so the sentence in the 409 is its only instruction. So the reason travels with the token now. getCoinpayLink returns none | needs_reconnect | connected, and the invoice route picks its message and its setup_instructions from that. The steps for a stale link lead with reconnecting and say what reconnecting does and does not change, because "your gigs are untouched" is the question anyone hesitates on. getConnectedCoinpayAccessToken stays, as a wrapper, so the wallets route and the bounty payout route are untouched. oauth_required still reports true for a worker in both cases: reconnecting is the same authorise round trip, so a client that keys on the flag to offer the button keeps working. The new coinpay_link_state field is what a caller should branch on. Two tests: the stale link must not receive the exact sentence that sent the reporter in circles, and a genuinely absent link must still be told to connect, so this cannot be "fixed" by wording everything as reconnect. 2104 tests pass across 219 files. Typecheck clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WxWrsbLuaSaZqQ7FFTrdVW
ThreatCrush Security Scan45 finding(s) HIGH/CRITICAL: 1 | MEDIUM: 8 | LOW: 36
Snippets are redacted; ThreatCrush never prints matched credential material. |
Merged
ralyodio
added a commit
that referenced
this pull request
Sep 9, 2026
Marks the CoinPay reconnect fix (#554): a worker whose link predated the wallet:read scope was told to connect an account they had already connected, and the invoice API now tells them to reconnect instead. Note for whoever cuts the next one: the tags have drifted a long way from the branch. v1.1.3 is 882 commits behind master, and package.json had already been moved to 1.1.4 by 1c4e430 without a tag ever being cut for it. So v1.1.5 is the first tag in a long while and it covers far more than its own line above. The deploy has never depended on this - Railway builds from the GitHub connection on merge - so nothing was broken by the drift, but the tag has not been a useful marker of what is live and this is a step back towards it. Claude-Session: https://claude.ai/code/session_01WxWrsbLuaSaZqQ7FFTrdVW Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reported from the agenticjobs apply flow: an agent tried to invoice $0.25 and ugig answered
CoinPay was connected. It tried again and got the same sentence.
The gate is right, the sentence is wrong
A CoinPay link authorised before ugig needed
wallet:readcannot read global wallet addresses, so it cannot back an invoice, andgetConnectedCoinpayAccessTokencorrectly returnsnullfor it.But
nullcarries no reason, so the route reported the only case it could name: not connected. That is an instruction to do something the worker has already done, which is exactly why it loops — they check, see a connection, and try again.#553 fixed this on the connections page, which now shows Reconnect required instead of Connected. The API never learned the difference, and the API is what an agent gets: it never sees that page, so the sentence in the 409 is its only instruction.
The reason now travels with the token
getCoinpayLink()returnsnone | needs_reconnect | connected, and the invoice route picks itserrorand itssetup_instructionsfrom that. For a stale link the steps lead with reconnecting and say what it does and does not change — "reconnecting re-authorises the same account, your gigs are untouched" is the question anyone hesitates on before clicking.Deliberately unchanged:
getConnectedCoinpayAccessTokenstays as a wrapper, so the wallets route and the bounty payout route are untouched.oauth_requiredstill reportstruefor a worker in both cases. Reconnecting is the same authorise round trip, so a client keying on that flag to offer the button keeps working.coinpay_link_statefield is what a caller should branch on.Tests
Two, and the second is the one that keeps the fix honest:
Checks
2104 tests pass across 219 files, 0 fail.
tsc --noEmitclean. Pre-commit hooks (lint + full Next build) pass.Not included
src/app/api/coinpay/wallets/route.tscarries the same connect-only wording. It is a different surface and nobody has reported hitting it, so I left it rather than widen this. Say the word and it is a two-line follow-up.🤖 Generated with Claude Code