Errors
Every BountyMesh method returns Result<T, Error>. There are 17 typed variants in the Error enum. All are declared at deploy time; new variants would shift discriminants and break SDK consumers with cached IDL.
pub enum Error {
SelfLoop,
MarketPaused,
RewardBelowMinimum,
InsufficientPayment,
TitleTooLong,
DescriptionTooLong,
AcceptanceTooLong,
PayloadTooLong,
IdSpaceExhausted,
BountyNotFound,
BountyNotOpen,
BountyNotClaimed,
BountyNotSubmitted,
BountyNotAccepted,
AlreadyWithdrawn,
Unauthorized,
ZeroHashRejected,
}Variant reference
Anti-cheat
SelfLoopErrormsg::source() == exec::program_id(). The program cannot call itself. Returned by every method as the first guard. Defensive; not reachable from any real user flow.
Operator config
MarketPausedErrorconfig.paused == true. Operator-controlled kill switch. Returned by every method as the second guard.
Post-specific
RewardBelowMinimumErrorreward < config.min_reward. Mainnet floor is 0.5 VARA (500_000_000_000 atomic). Returned by Post.
InsufficientPaymentErrormsg::value() < reward. Caller didn't attach enough VARA. Returned by Post.
TitleTooLongErrortitle.len() > 200. Returned by Post.
DescriptionTooLongErrordescription.len() > 2000. Returned by Post.
AcceptanceTooLongErroracceptance.len() > 1000. Returned by Post.
IdSpaceExhaustedErrornext_id.checked_add(1) overflowed u64::MAX. Unreachable at any realistic scale. Returned by Post.
Submit-specific
PayloadTooLongErrorresult_payload.len() > 5000. Returned by Submit.
ZeroHashRejectedErrorresult_hash == H256::zero(). Catches accidentally-uninitialized hashes. Returned by Submit.
State-transition guards
BountyNotFoundErrorid doesn't exist in bounties. Returned by Claim, Submit, Accept, Withdraw.
BountyNotOpenErrorCaller tried to claim a bounty whose status is not Open. Returned by Claim.
BountyNotClaimedErrorCaller tried to submit against a bounty whose status is not Claimed. Returned by Submit.
BountyNotSubmittedErrorCaller tried to accept a bounty whose status is not Submitted. Returned by Accept.
BountyNotAcceptedErrorCaller tried to withdraw from a bounty whose status is not Accepted. Returned by Withdraw.
Authorization
UnauthorizedErrorCaller's msg::source() doesn't match the expected actor. Returned by:
- Submit when caller != bounty.worker
- Accept when caller != bounty.poster
- Withdraw when caller != bounty.worker
Idempotency
AlreadyWithdrawnErrorbounty.withdrawn == true. The worker already pulled the reward. Returned by Withdraw.
Per-method error matrix
| Error | Post | Claim | Submit | Accept | Withdraw |
|---|---|---|---|---|---|
SelfLoop | ✓ | ✓ | ✓ | ✓ | ✓ |
MarketPaused | ✓ | ✓ | ✓ | ✓ | ✓ |
RewardBelowMinimum | ✓ | – | – | – | – |
InsufficientPayment | ✓ | – | – | – | – |
TitleTooLong | ✓ | – | – | – | – |
DescriptionTooLong | ✓ | – | – | – | – |
AcceptanceTooLong | ✓ | – | – | – | – |
IdSpaceExhausted | ✓ | – | – | – | – |
BountyNotFound | – | ✓ | ✓ | ✓ | ✓ |
BountyNotOpen | – | ✓ | – | – | – |
BountyNotClaimed | – | – | ✓ | – | – |
BountyNotSubmitted | – | – | – | ✓ | – |
BountyNotAccepted | – | – | – | – | ✓ |
Unauthorized | – | – | ✓ | ✓ | ✓ |
ZeroHashRejected | – | – | ✓ | – | – |
PayloadTooLong | – | – | ✓ | – | – |
AlreadyWithdrawn | – | – | – | – | ✓ |
SDK error handling pattern
The generated BountyMeshError union is exported from @bountymesh/sdk/errors. Use 'ok' in reply for discrimination:
const result = await sdk.post(signer, params);
if ('ok' in result.reply) {
console.log("bounty id", result.reply.ok);
} else {
switch (result.reply.err) {
case 'RewardBelowMinimum':
console.error("bump reward above 0.5 VARA");
break;
case 'InsufficientPayment':
console.error("attach more value");
break;
default:
console.error("unexpected:", result.reply.err);
}
}The error type is generated from the IDL snapshot by scripts/generate-errors.ts. Drift caught by make sdk-check-codegen-drift. Never hand-edit src/errors.generated.ts. See SDK reference.
Refund posture on every Err
All Err branches refund the attached msg::value() via CommandReply::with_value(value). This is the canonical refund-correctness primitive — see Anti-cheat.
msg::send_bytes is NOT used on Err branches. In sails-rs 0.10, outbound messages only flush on successful execution; using msg::send_bytes for Err refunds would silently drop the value into the program balance with no on-chain record. The reply pattern is atomic with the Err return.
Source
programs/bountymesh/app/src/errors.rs— the enumprograms/bountymesh/app/src/service.rs— every guard, inline-documented
Next steps
- Anti-cheat — refund correctness, self-loop, refund posture
- SDK reference — generated error types
- Events — emitted variants