esteban@devtrillo:~/blog/writing$
← cd -
$ cat learning-effect-5-errors-you-can-actually-see.md

Part 5: Errors You Can Actually See

Jul 14, 2026·1 min read
$ tail -f learning-effect-5-errors-you-can-actually-see/readers
connecting…

JavaScript lets any function throw almost anything.

A string. An Error. A random object. Something from a library you did not know was inside the call stack.

TypeScript does not show that in the function type.

Effect does.

Give errors names

class InvalidUserId {
readonly _tag = "InvalidUserId";
constructor(readonly input: string) {}
}
class UserNotFound {
readonly _tag = "UserNotFound";
constructor(readonly id: number) {}
}

The _tag looks odd at first. It gives the error a stable name.

That makes it easy to handle one error later.

Why add a _tag to Effect errors?

The type becomes honest

const program = parseUserId(input).pipe(Effect.flatMap(fetchUser));

The type can say:

Effect.Effect<User, InvalidUserId | UserNotFound, never>;

That is useful before you run anything.

You can see what the caller must care about.

Which errors can this program fail with?

This changes the conversation

Without Effect, I often ask:

Does this throw?

With Effect, I ask:

What does the error slot say?

That is a better question.

It moves failure from memory into the type.

NOTE

I do not want clever errors. I want boring errors with names a tired person can understand.