Action Retrier
Idempotent action retries
git clone https://github.com/bijectionhq/action-retrier.git
cd action-retrier
git checkout 22d06795ce7788dc4f8bde1721171c27e5b285a8Bijection repository access is required. Request access.
Description
Idempotent action retries
Explore the package README for API concepts, configuration, and examples. Check the Bijection source for adaptation-specific setup.
Review the source and its dependencies before adopting this package. Source availability does not establish npm publication or runtime qualification.
Getting started
- Get the source at the revision above and read its package documentation.
- Check the required Bijection SDK version, dependencies, and external service credentials.
- Follow the package setup instructions and the guide to using components.
- Exercise the component in your deployment according to your project’s verification rules.
Open source
Maintained source: bijectionhq/action-retrier. Based on convex/action-retrier. License: Apache-2.0. Consult the repository for license terms, notices, and contributor attribution.
From the upstream publisher
Convex component for retrying idempotent actions.
Upstream overview
From the upstream package documentation.
The Action Retrier component automatically retries failed Convex actions with exponential backoff, making external API calls and network operations more reliable. It handles transient failures by running your action up to a configurable number of times before giving up. You get async tracking of run status, cancellation support, and optional completion callbacks.
Use cases
From the upstream package documentation.
- Third-party API integration - Retry payment processing, email sending, or webhook calls that fail due to network timeouts or service unavailability
- External service dependencies - Handle flaky connections to databases, file storage, or authentication providers that occasionally return 5xx errors
- Distributed system resilience - Automatically recover from transient network partitions or temporary service degradation without manual intervention
- Batch processing workflows - Ensure data synchronization tasks complete successfully even when individual operations encounter intermittent failures
- Critical business operations - Add reliability guarantees to actions that must eventually succeed, like order fulfillment or user onboarding steps
How it works
From the upstream package documentation.
Install the component into your Convex app configuration, then create an ActionRetrier instance pointing to the component. Call retrier.run(ctx, actionReference, args) from mutations or actions to execute your target action with automatic retry logic.
The component uses exponential backoff with configurable parameters: initialBackoffMs for the first retry delay, base for the backoff multiplier, and maxFailures for retry limits. Failed runs sleep progressively longer between attempts until success or exhaustion.
Run tracking happens through a returned RunId that lets you query status with retrier.status(), cancel execution with retrier.cancel(), or clean up completed runs with retrier.cleanup(). Optional onComplete mutation callbacks execute exactly once when runs finish, regardless of success or failure.
Upstream package FAQ
Does Action Retrier work with all external APIs?
Action Retrier works with any external API call made from Convex actions, but it's designed for idempotent operations. You configure which error types trigger retries and which should fail immediately.
How does retry logic affect action execution time?
Action Retrier uses exponential backoff, so retry delays increase with each attempt. The total execution time depends on your configured retry count and base delay. Failed retries will extend your action's runtime but improve reliability.
Can I customize retry behavior for different services?
Yes, Action Retrier allows per-service configuration of retry attempts, delay intervals, and which error conditions should trigger retries versus immediate failure.
What happens if all retry attempts fail?
When Action Retrier exhausts all configured retry attempts, it throws the last error encountered. Your Convex action can then handle this final failure state according to your application logic.
Use with agents and CLI
Give your coding agent the component’s source identity and setup guidance.
Help me integrate Action Retrier with Bijection.
Read: https://bijection.com/components/action-retrier/SKILL.md
Check SDK compatibility and dependency availability.
Follow this project's execution and verification rules.Upstream README.md
View repository ↗Documentation from convex/action-retrier, recorded October 23, 2026. APIs and setup may differ in the Bijection adaptation.
Actions can sometimes fail due to network errors, server restarts, or issues with a 3rd party API, and it's often useful to retry them. The Action Retrier component makes this really easy.
import { ActionRetrier } from "@convex-dev/action-retrier";
import { components } from "./convex/_generated/server";
const retrier = new ActionRetrier(components.actionRetrier);
// `retrier.run` will automatically retry your action up to four times before giving up.
await retrier.run(ctx, internal.module.myAction, { arg: 123 });
The retrier component will run the action and retry it on failure, sleeping with exponential backoff, until the action succeeds or the maximum number of retries is reached.
Pre-requisite: Convex
You'll need an existing Convex project to use the component. Convex is a hosted backend platform, including a database, serverless functions, and a ton more you can learn about here.
Run npm create convex or follow any of the
quickstarts to set one up.
Installation
First, add @convex-dev/action-retrier as an NPM dependency:
npm install @convex-dev/action-retrier
Then, install the component into your Convex project within the
convex/convex.config.ts configuration file:
// convex/convex.config.ts
import { defineApp } from "convex/server";
import actionRetrier from "@convex-dev/action-retrier/convex.config.js";
const app = defineApp();
app.use(actionRetrier);
export default app;
Finally, create a new ActionRetrier within your Convex project, and point it
to the installed component:
// convex/index.ts
import { ActionRetrier } from "@convex-dev/action-retrier";
import { components } from "./_generated/api";
export const retrier = new ActionRetrier(components.actionRetrier);
You can optionally configure the retrier's backoff behavior in the
ActionRetrier constructor.
const retrier = new ActionRetrier(components.actionRetrier, {
initialBackoffMs: 10000,
base: 10,
maxFailures: 4,
});
initialBackoffMsis the initial delay after a failure before retrying (default: 250).baseis the base for the exponential backoff (default: 2).maxFailuresis the maximum number of times to retry the action (default: 4).
API
Starting a run
After installing the component, use the run method from either a mutation or
action to kick off an action.
export const kickoffExampleAction = mutation({
handler: async (ctx) => {
const runId = await retrier.run(ctx, internal.index.exampleAction, {
foo: "bar",
});
// ... optionally persist or pass along the runId
},
});
export const exampleAction = internalAction({
args: { foo: v.string() },
handler: async (ctx, args) => {
return operationThatMightFail(args);
},
});
The return value of retrier.run is not the result of the action, but rather an
ID that you can use to query its status or cancel it. The action's return value
is saved along with the status, when it succeeds.
You can optionally specify overrides to the backoff parameters in an options argument.
export const kickoffExampleAction = action(async (ctx) => {
const runId = await retrier.run(
ctx,
internal.index.exampleAction,
{ failureRate: 0.8 },
{
initialBackoffMs: 125,
base: 2.71,
maxFailures: 3,
},
);
});
You can specify an onComplete mutation callback in the options argument as
well. This mutation is guaranteed to eventually run exactly once.
// convex/index.ts
import { runResultValidator, runIdValidator } from "@convex-dev/action-retrier";
export const kickoffExampleAction = action(async (ctx) => {
const runId = await retrier.run(
ctx,
internal.index.exampleAction,
{ failureRate: 0.8 },
{
onComplete: internal.index.exampleCallback,
},
);
});
export const exampleCallback = internalMutation({
args: { id: runIdValidator, result: runResultValidator },
handler: async (ctx, args) => {
if (args.result.type === "success") {
console.log(
"Action succeeded with return value:",
args.result.returnValue,
);
} else if (args.result.type === "failed") {
console.log("Action failed with error:", args.result.error);
} else if (args.result.type === "canceled") {
console.log("Action was canceled.");
}
},
});
Run status
The run method returns a RunId, which can then be used for querying a run's
status.
export const kickoffExampleAction = action(async (ctx) => {
const runId = await retrier.run(ctx, internal.index.exampleAction, {
failureRate: 0.8,
});
while (true) {
const status = await retrier.status(ctx, runId);
if (status.type === "inProgress") {
await new Promise((resolve) => setTimeout(resolve, 1000));
continue;
} else {
console.log("Run completed with result:", status.result);
break;
}
}
});
Canceling a run
You can cancel a run using the cancel method.
export const kickoffExampleAction = action(async (ctx) => {
const runId = await retrier.run(ctx, internal.index.exampleAction, {
failureRate: 0.8,
});
await new Promise((resolve) => setTimeout(resolve, 1000));
await retrier.cancel(ctx, runId);
});
Runs that are currently executing will be canceled best effort, so they may
still continue to execute. A succcesful call to cancel, however, does
guarantee that subsequent status calls will indicate cancelation.
Cleaning up completed runs
Runs take up space in the database, since they store their return values. After
a run completes, you can immediately clean up its storage by using
retrier.cleanup(ctx, runId). The system will automatically cleanup completed
runs after 7 days.
export const kickoffExampleAction = action(async (ctx) => {
const runId = await retrier.run(ctx, internal.index.exampleAction, {
failureRate: 0.8,
});
try {
while (true) {
const status = await retrier.status(ctx, runId);
if (status.type === "inProgress") {
await new Promise((resolve) => setTimeout(resolve, 1000));
continue;
} else {
console.log("Run completed with result:", status.result);
break;
}
}
} finally {
await retrier.cleanup(ctx, runId);
}
});
Logging
You can set the ACTION_RETRIER_LOG_LEVEL to DEBUG to have the retrier log
out more of its internal information, which you can then view on the Convex
dashboard.
npx convex env set ACTION_RETRIER_LOG_LEVEL DEBUG
The default log level is INFO, but you can also set it to ERROR for even
fewer logs.