Skip to main content

Overview

runPromiseUnwrapped is a convenience wrapper around Effect’s runPromiseExit that automatically throws errors in a more conventional format. Instead of wrapping errors in Effect’s Cause type, it directly throws the underlying error value.

Signature

function runPromiseUnwrapped<A, E>(
  effect: Effect.Effect<A, E, never>
): Promise<A>

Parameters

effect
Effect.Effect<A, E, never>
required
The Effect to execute. Must have no remaining service requirements (the R parameter must be never).

Returns

Returns a Promise<A> that:
  • Resolves with the success value if the Effect succeeds
  • Rejects with the error value (unwrapped from Cause) if the Effect fails
  • Rejects with the Cause itself if it’s not a standard failure (e.g., defects, interruptions)

Usage

Basic Success Case

import { runPromiseUnwrapped } from 'ff-effect';
import { Effect } from 'effect';

const result = await runPromiseUnwrapped(
  Effect.succeed('hello')
);

console.log(result); // 'hello'

Error Handling

import { runPromiseUnwrapped } from 'ff-effect';
import { Effect } from 'effect';

class MyError extends Error {
  constructor(message: string) {
    super(message);
  }
}

try {
  await runPromiseUnwrapped(
    Effect.fail(new MyError('Something went wrong'))
  );
} catch (error) {
  // error is MyError, not Cause<MyError>
  console.error(error.message); // 'Something went wrong'
}

Comparison with runPromiseExit

Here’s how runPromiseUnwrapped differs from using runPromiseExit directly:
import { runPromiseUnwrapped } from 'ff-effect';
import { Effect } from 'effect';

try {
  const result = await runPromiseUnwrapped(
    Effect.fail(new Error('boom'))
  );
} catch (error) {
  // error is Error directly
  console.error(error.message); // 'boom'
}

Implementation Details

The function works by:
  1. Running the Effect with Effect.runPromiseExit
  2. Matching on the resulting Exit:
    • If success: return the value
    • If failure: check if it’s a standard failure (Cause.isFailType)
      • If yes: throw the unwrapped error
      • If no: throw the entire Cause (for defects, interruptions, etc.)
src/run-promise-unwrapped.ts
import { Cause, Effect, Exit } from 'effect';

export async function runPromiseUnwrapped<A, E>(
  effect: Effect.Effect<A, E, never>,
) {
  const exit = await Effect.runPromiseExit(effect);
  return Exit.match(exit, {
    onSuccess: (value) => value,
    onFailure: (cause) => {
      throw Cause.isFailType(cause) ? cause.error : cause;
    },
  });
}

When to Use

Use runPromiseUnwrapped when integrating Effect code with traditional Promise-based code that expects standard error throwing behavior.

Good Use Cases

API Routes / HTTP Handlers
app.get('/users/:id', async (req, res) => {
  try {
    const user = await runPromiseUnwrapped(
      fetchUser(req.params.id)
    );
    res.json(user);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});
Test Utilities
test('should fetch user', async () => {
  const user = await runPromiseUnwrapped(
    userService.findById(1)
  );
  expect(user.name).toBe('Alice');
});
Legacy Code Integration
// Existing function expects promises with throw semantics
async function legacyHandler(getId: () => Promise<number>) {
  const id = await getId();
  // ...
}

// Wrap your Effect
await legacyHandler(() =>
  runPromiseUnwrapped(myEffect)
);

When NOT to Use

Within Effect Code Inside an Effect context, use normal Effect error handling:
// DON'T do this
const program = Effect.gen(function* () {
  try {
    const result = await runPromiseUnwrapped(someEffect);
  } catch (error) {
    // ...
  }
});

// DO this instead
const program = Effect.gen(function* () {
  const result = yield* someEffect.pipe(
    Effect.catchAll((error) => /* handle error */)
  );
});
When You Need Structured Error Information If you need to distinguish between different failure types (defects, interruptions, failures), use runPromiseExit instead.

Error Types

The function handles different Cause types:

Standard Failures

Throws the unwrapped error:
const effect = Effect.fail(new Error('boom'));
// Throws: Error('boom')

Defects

Throws the entire Cause:
const effect = Effect.die(new Error('unexpected'));
// Throws: Cause (containing the defect)

Interruptions

Throws the Cause:
const effect = Effect.interrupt;
// Throws: Cause (representing interruption)

See Also