Skip to main content
When things go wrong, this guide helps you identify and fix common Runner issues quickly.

Quick Error Reference

Symptom: Task call fails at runtimeLikely Cause: Forgot .build() on task/resource definitionFix:
// Wrong
const myTask = r.task("app.task").run(async () => {});

// Correct
const myTask = r.task("app.task").run(async () => {}).build();
Symptom: Runtime crash during initializationLikely Cause: Component not registeredFix:
const database = r.resource("app.db").build();
const myTask = r.task("app.task").dependencies({ database }).build();

// Wrong - database not registered
const app = r.resource("app").register([myTask]).build();

// Correct - both registered
const app = r.resource("app").register([database, myTask]).build();
Symptom: Startup crash before app runsLikely Cause: Missing .with() config for resourceFix:
const server = r
  .resource<{ port: number }>("app.server")
  .init(async ({ port }) => startServer(port))
  .build();

// Wrong - no config provided
const app = r.resource("app").register([server]).build();

// Correct - config provided
const app = r.resource("app").register([server.with({ port: 3000 })]).build();
Symptom: run(app) fails with runner.errors.visibilityViolationLikely Cause: Cross-resource reference to a non-exported itemFix:
const internalTask = r.task("billing.internal").run(async () => 1).build();
const publicTask = r.task("billing.public").run(async () => 2).build();

const billing = r
  .resource("billing")
  .register([internalTask, publicTask])
  .exports([publicTask]) // Only publicTask is accessible outside
  .build();

// Wrong - trying to depend on non-exported item
const app = r
  .resource("app")
  .register([billing])
  .dependencies({ internalTask }) // Error!
  .build();

// Correct - depend on exported item
const app = r
  .resource("app")
  .register([billing])
  .dependencies({ publicTask })
  .build();
Symptom: run(app) fails or TypeScript inference failsLikely Cause: Actual dependency cycle or import cycleFix for runtime cycle: Break the dependency loop across tasks/resources:
// Bad - A depends on B, B depends on A
const resourceA = r.resource("a").dependencies({ resourceB }).build();
const resourceB = r.resource("b").dependencies({ resourceA }).build();

// Good - Break the cycle
const shared = r.resource("shared").build();
const resourceA = r.resource("a").dependencies({ shared }).build();
const resourceB = r.resource("b").dependencies({ shared }).build();
Fix for type inference cycle: Use explicit type annotation:
import type { IResource } from "@bluelibs/runner";

const resourceA = r
  .resource("a")
  .dependencies({ resourceB })
  .build() as IResource<void, { value: string }>;
Symptom: Task hangs then throwsLikely Cause: Operation exceeded timeout TTLFix:
// Increase timeout or investigate slow operation
const slowTask = r
  .task("app.slow")
  .middleware([
    globals.middleware.task.timeout.with({ ttl: 10000 }), // Increase from 5s to 10s
  ])
  .run(async () => await slowOperation())
  .build();
Symptom: Task crashes mid-executionLikely Cause: Dependency not properly injectedFix:
// Check dependencies match what you use
const myTask = r
  .task("app.task")
  .dependencies({ db, logger }) // Must declare all dependencies
  .run(async (input, { db, logger }) => {
    // Both db and logger are available
  })
  .build();

Common First Failures

New to Runner? These are mistakes everyone makes when learning the framework.

Forgot .build()

// Wrong - returns a builder, not a usable task
const myTask = r.task("app.task").run(async () => "hello");

// Correct - returns the actual task
const myTask = r
  .task("app.task")
  .run(async () => "hello")
  .build(); // Required!
Symptom: TypeError: myTask is not a function or strange type errors.

Forgot to Register

const database = r.resource("app.db").init(async () => connection).build();
const myTask = r
  .task("app.task")
  .dependencies({ database })
  .run(async (_, { database }) => database.query())
  .build();

// Wrong - myTask depends on database but database isn't registered
const app = r.resource("app").register([myTask]).build();

// Correct - register ALL components
const app = r.resource("app").register([database, myTask]).build();
Remember: dependencies says “I need these” — register says “these exist”.

Missing .with() Config

// Resource requires configuration
const server = r
  .resource<{ port: number }>("app.server")
  .init(async ({ port }) => startServer(port))
  .build();

// Wrong - no config provided (TypeScript error)
const app = r.resource("app").register([server]).build();

// Correct - provide config with .with()
const app = r
  .resource("app")
  .register([server.with({ port: 3000 })])
  .build();

Calling Task Before Runtime

// Wrong - can't call task directly without runtime
const result = await myTask({ input: "data" }); // Fails!

// Correct - get runtime first, then call
const { runTask, dispose } = await run(app);
const result = await runTask(myTask, { input: "data" });
await dispose();

Debug Mode

Enable verbose debugging to see what’s happening:
import { run } from "@bluelibs/runner";

const { runTask, dispose } = await run(app, {
  debug: "verbose",
  logs: { printThreshold: "debug" },
});
What you’ll see:
[DEBUG] [runner] Initializing resource: app.database
[DEBUG] [runner] Resource initialized: app.database (12ms)
[DEBUG] [runner] Executing task: app.tasks.createUser
[DEBUG] [runner]   Input: { "name": "Ada", "email": "[email protected]" }
[DEBUG] [runner]   Result: { "id": "user-123", "name": "Ada" }
[DEBUG] [runner] Task completed: app.tasks.createUser (5ms)

Debug Levels

LevelWhat’s Logged
"normal"Lifecycle events, errors, event emissions
"verbose"All of above + task inputs/outputs, resource configs

Per-Component Debugging

import { globals } from "@bluelibs/runner";

const criticalTask = r
  .task("app.payment")
  .tags([globals.tags.debug.with({ logTaskInput: true, logTaskOutput: true })])
  .run(async (input) => processPayment(input))
  .build();

Lifecycle Issues

Symptom: Hanging process, “port already in use” on restart, connection leaksFix: Ensure every resource with setup has matching cleanup:
const server = r
  .resource<{ port: number }>("app.server")
  .init(async ({ port }) => {
    const app = express();
    const listener = app.listen(port);
    return { app, listener };
  })
  .dispose(async ({ listener }) => {
    // Don't forget this!
    return new Promise((resolve) => listener.close(resolve));
  })
  .build();
Symptom: dispose() never resolvesLikely causes:
  1. Dispose function has unresolved promise
  2. Resource left open handles (timers/sockets)
  3. Circular await in dispose chain
Debug approach:
const { dispose } = await run(app);

// Add timeout to identify hanging dispose
const timeout = setTimeout(() => {
  console.error("Dispose hanging - check resource cleanup");
  process.exit(1);
}, 10000);

await dispose();
clearTimeout(timeout);

Performance Issues

Check middleware order — faster middleware should come first:
// Good - fast checks first
.middleware([
  authCheck,        // ~0.1ms - fails fast if unauthorized
  rateLimit,        // ~0.5ms - blocks before expensive work
  timeout,          // wraps the slow stuff
  expensiveLogging, // can afford to be slow
])
Enable verbose logging to see cache behavior:
await run(app, { debug: "verbose" });
// Watch for: "Cache miss for app.tasks.expensive" vs "Cache hit"
Profile startup time:
const start = Date.now();
const { dispose } = await run(app);
console.log(`App initialized in ${Date.now() - start}ms`);
Common causes:
  • Sequential resource initialization (use initMode: "parallel")
  • Expensive operations in init() functions
  • Network calls during startup

TypeScript Issues

Symptom: TypeScript shows any or fails to infer types in circular importsSolution: Explicitly type the resource that closes the loop:
import type { IResource } from "@bluelibs/runner";

export const problematicResource = r
  .resource("app.problematic")
  .dependencies({ otherResource })
  .init(async (_, { otherResource }) => {
    return { value: otherResource.something };
  })
  .build() as IResource<void, { value: string }>;
Symptom: Task input/output types don’t match middleware expectationsFix: Ensure task satisfies all middleware contracts:
// Middleware expects { user: { role: string } } input
const authMiddleware = r.middleware.task<
  { requiredRole: string },
  { user: { role: string } },
  unknown
>("auth");

// Task MUST have compatible input type
const adminTask = r
  .task("admin")
  .middleware([authMiddleware.with({ requiredRole: "admin" })])
  .run(async (input: { user: { role: string }; /* other fields */ }) => {
    // input.user.role is available and typed
  })
  .build();

Platform-Specific Issues

Symptom: PlatformUnsupportedFunction when using async contextCause: Async context requires Node.js AsyncLocalStorageSolution: Use Node.js or redesign to avoid request-scoped state:
// Only works in Node.js
const requestContext = r
  .asyncContext<{ requestId: string }>("app.ctx.request")
  .build();

// In browser/edge, pass context explicitly instead
const myTask = r
  .task("app.task")
  .run(async (input: { requestId: string; data: any }) => {
    // Use input.requestId instead of context
  })
  .build();
Symptom: Import error or runtime error when using durable workflowsCause: Durable workflows are Node.js-only (@bluelibs/runner/node)Solution: Use Node.js runtime for durable workflows, or redesign using tasks + external job queue.

Filing a Good Issue

When you need help, include this information:
## Environment

- @bluelibs/runner version: X.X.X
- Node.js version: X.X.X
- TypeScript version: X.X.X
- OS: macOS/Windows/Linux

## Minimal Reproduction

```typescript
// Smallest possible code that reproduces the issue
import { r, run } from "@bluelibs/runner";

const app = r.resource("app").build();
await run(app); // Describe what goes wrong here

Expected Behavior

What should happen.

Actual Behavior

What actually happens.

Error Output

Full stack trace here

Debug Logs

Output from: await run(app, { debug: "verbose" })

**Get your version:**

```bash
npm ls @bluelibs/runner
Pro tips:
  • Minimal reproduction > walls of code
  • Stack traces > “it doesn’t work”
  • Debug logs often reveal the issue before you file

Additional Resources

GitHub Issues

Search existing issues or file a new one

Examples

Reference working examples

Migration Guide

Upgrading between versions

Production Readiness

Pre-deployment checklist

Still Stuck?

  1. Search existing issues: GitHub Issues
  2. Check examples: Examples directory
  3. Ask the AI: Runner Chatbot
  4. Open an issue: New Issue
  5. Enterprise support: Contact us

Build docs developers (and LLMs) love