Skip to content

Commit 6bc6976

Browse files
committed
fix(services/opencode): reduce Bugsnag noise from gift card claim races
Add isGiftCardAlreadyClaimed to StaleState so submitIntent can skip Bugsnag reporting when a gift card was claimed between the pre-check and intent submission — a possible race. This was made relatively impossible by 223bb41. Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 57d09ad commit 6bc6976

3 files changed

Lines changed: 36 additions & 2 deletions

File tree

services/opencode/src/main/kotlin/com/getcode/opencode/internal/domain/repositories/InternalTransactionRepository.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.getcode.opencode.model.transactions.SwapFundingSource
1212
import com.getcode.opencode.model.transactions.SwapRequest
1313
import com.getcode.opencode.model.transactions.TransactionMetadata
1414
import com.getcode.opencode.model.transactions.WithdrawalAvailability
15+
import com.getcode.opencode.model.core.errors.SubmitIntentError
1516
import com.getcode.opencode.repositories.TransactionRepository
1617
import com.getcode.opencode.solana.intents.IntentType
1718
import com.getcode.solana.keys.Mint
@@ -29,7 +30,12 @@ internal class InternalTransactionRepository @Inject constructor(
2930
intent: IntentType,
3031
owner: Ed25519.KeyPair
3132
): Result<IntentType> = service.submitIntent(scope, intent, owner)
32-
.onFailure { ErrorUtils.handleError(it) }
33+
.onFailure {
34+
// Expected race: pre-claim check passes but the gift card is claimed
35+
// before the intent is submitted. Not a bug — skip Bugsnag reporting.
36+
if (it is SubmitIntentError.StaleState && it.isGiftCardAlreadyClaimed) return@onFailure
37+
ErrorUtils.handleError(it)
38+
}
3339

3440
override suspend fun getIntentMetadata(
3541
intentId: PublicKey,

services/opencode/src/main/kotlin/com/getcode/opencode/model/core/errors/Errors.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,10 @@ sealed class SubmitIntentError(
122122
if (details.isNotEmpty()) append(": ${details.joinToString()}")
123123
}), NotifiableError
124124
data class StaleState(private val reasons: List<String>) :
125-
SubmitIntentError(message = reasons.joinToString()), NotifiableError
125+
SubmitIntentError(message = reasons.joinToString()), NotifiableError {
126+
val isGiftCardAlreadyClaimed: Boolean
127+
get() = reasons.any { it.contains("gift card balance has already been claimed") }
128+
}
126129

127130
data class Denied(private val reasons: List<String>) :
128131
SubmitIntentError(message = reasons.joinToString())

services/opencode/src/test/kotlin/com/getcode/opencode/model/core/errors/SubmitIntentErrorTest.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.codeinc.opencode.gen.transaction.v1.reasonStringErrorDetails
66
import com.codeinc.opencode.gen.transaction.v1.deniedErrorDetails
77
import kotlin.test.Test
88
import kotlin.test.assertEquals
9+
import kotlin.test.assertFalse
910
import kotlin.test.assertIs
1011
import kotlin.test.assertTrue
1112

@@ -146,6 +147,30 @@ class SubmitIntentErrorTest {
146147
errors.forEach { assertTrue(it is Throwable) }
147148
}
148149

150+
@Test
151+
fun staleStateWithGiftCardClaimedReasonIsGiftCardAlreadyClaimed() {
152+
val error = SubmitIntentError.typed(
153+
buildError(
154+
SubmitIntentResponse.Error.Code.STALE_STATE,
155+
reasonStrings = listOf("gift card balance has already been claimed")
156+
)
157+
)
158+
assertIs<SubmitIntentError.StaleState>(error)
159+
assertTrue(error.isGiftCardAlreadyClaimed)
160+
}
161+
162+
@Test
163+
fun staleStateWithOtherReasonIsNotGiftCardAlreadyClaimed() {
164+
val error = SubmitIntentError.typed(
165+
buildError(
166+
SubmitIntentResponse.Error.Code.STALE_STATE,
167+
reasonStrings = listOf("nonce expired")
168+
)
169+
)
170+
assertIs<SubmitIntentError.StaleState>(error)
171+
assertFalse(error.isGiftCardAlreadyClaimed)
172+
}
173+
149174
@Test
150175
fun otherWrausesCause() {
151176
val cause = RuntimeException("root cause")

0 commit comments

Comments
 (0)