---
title: "Raw MIME Address Parsing (parser/address)"
canonical: "https://test.abhinandan.one/node-sdk-parsing-email/node-sdk-address-parsing"
markdown_url: "https://test.abhinandan.one/node-sdk-parsing-email/node-sdk-address-parsing.md"
publisher: "Primitive SDKs"
kind: "reference"
content_type: "reference"
category: "Node.js SDK"
parent: "node-sdk-parsing-email"
description: "parseFromHeader strictly validates a single RFC 5322 address for authorization gates, while parseFromHeaderLoose returns a best-effort address plus display name for display-only use."
keywords: ["parseFromHeader", "parseFromHeaderLoose", "ParseFromHeaderResult", "ParseFromHeaderFailureReason", "ValidatedAddress", "ParsedAddress"]
last_modified: "2026-08-21T18:22:43.359885+00:00"
published_at: "2026-08-11T18:54:58.809606+00:00"
source_files:
  - "sdk-node/src/parser/address-parser.ts"
sections:
  - {anchor: "what-the-address-parsers-do", title: "What the address parsers do"}
  - {anchor: "parsefromheader", title: "parseFromHeader"}
  - {anchor: "signature", title: "Signature"}
  - {anchor: "result-shape", title: "Result shape"}
  - {anchor: "rejection-reasons", title: "Rejection reasons"}
  - {anchor: "normalization", title: "Normalization"}
  - {anchor: "parsefromheaderloose", title: "parseFromHeaderLoose"}
---

> Documentation index: https://test.abhinandan.one/llms.txt

# Raw MIME Address Parsing (parser/address)

Reference for parseFromHeader and parseFromHeaderLoose, the two RFC 5322 address parsers in @primitivedotdev/sdk/parser that extract a single validated address from a From/Sender header.

## What the address parsers do

Two address parsers ship under `@primitivedotdev/sdk/parser`: `parseFromHeader`, a strict RFC 5322 parser meant for authorization decisions, and `parseFromHeaderLoose`, a lenient parser meant for display-only call sites. Both operate on a raw header string (e.g. the `From` or `Sender` header value) and are implemented in `sdk-node/src/parser/address-parser.ts`.

> **Warning:** Never use `parseFromHeaderLoose` for permission gates or any decision that grants access. Its recovered display name can silently absorb a second address or garbage tokens. Use `parseFromHeader` for anything security-bearing.

## parseFromHeader

Strictly parses a header for security-bearing contexts: allowlist gates, permission grants, [domain-anchored sender trust](https://test.abhinandan.one/node-sdk-email-authenticity.md) anchors, and similar decisions where the address itself is the thing being trusted.

```typescript
import { parseFromHeader } from "@primitivedotdev/sdk/parser";

const result = parseFromHeader('"Alice" <alice@example.com>');

if (result.ok) {
  console.log(result.value.address); // "alice@example.com"
} else {
  console.log(result.reason); // one of ParseFromHeaderFailureReason
}
```

### Signature

```typescript
function parseFromHeader(
  header: string | null | undefined,
): ParseFromHeaderResult;
```

### Result shape

```typescript
type ParseFromHeaderResult =
  | { ok: true; value: ValidatedAddress }
  | { ok: false; reason: ParseFromHeaderFailureReason };

interface ValidatedAddress {
  address: string; // lowercased local-part and domain
}
```

### Rejection reasons

`parseFromHeader` never falls back to a "best guess." Any of the following inputs return `{ ok: false, reason }` instead of a partially-parsed address:

| `reason` | Trigger |
|---|---|
| `empty` | `header` is `null`, `undefined`, or whitespace-only after trimming |
| `too_long` | Header exceeds 998 UTF-8 bytes (the RFC 5322 §2.1.1 line limit, measured in bytes so multi-byte SMTPUTF8/RFC 6531 characters can't bypass the cap) |
| `multiple_addresses` | Header contains more than one address (RFC 5322 permits multi-address `From`, but it's rare and ambiguous as an identity) |
| `group_syntax` | Header uses group syntax, e.g. `Friends: a@b.com, c@d.com;` |
| `invalid_address` | The single address fails validator's `isEmail` check under the SDK's chosen options, or the parse produced no usable entry |

The `isEmail` check (via `validator/lib/isEmail`) enforces per-part length limits (64-octet local-part, 255-octet domain), dot-atom rules, hostname-label rules, a required TLD, and other RFC 5321/5322 conformance checks. Its options:

| Option | Value | Effect |
|---|---|---|
| `allow_ip_domain` | `true` | Accepts address-literals like `user@[192.168.1.1]` |
| `require_tld` | `true` | Rejects `user@localhost` |
| `allow_display_name` | `false` | Only the bare addr-spec is checked (the display name was already stripped by the header parser) |
| `allow_utf8_local_part` | `true` | Accepts SMTPUTF8 / EAI local-parts |

### Normalization

On success, `value.address` is lowercased in full, both the local-part and the domain. RFC 5321 §2.4 technically permits case-sensitive local-parts, but every consumer mailbox treats them as case-insensitive in practice; a case-sensitive grant key would otherwise split `Bob@x.com` from `bob@x.com` into separate rows and defeat a primary-key lookup.

No display name is returned. Deliberately: an input like `Name <user@x.com> <attacker@y.com>` parses as one entry whose `name` field can silently absorb the second address, so surfacing that as a "parsed name" here would invite misuse. Call `parseFromHeaderLoose` alongside if you also need a name for display.

## parseFromHeaderLoose

Lenient parser for display-only call sites: inbox card "from" fields, log lines, debugging output.

```typescript
import { parseFromHeaderLoose } from "@primitivedotdev/sdk/parser";

const parsed = parseFromHeaderLoose('"Alice" <alice@example.com>');
// { address: "alice@example.com", name: "Alice" }
```

Given a multi-address header, it returns the first address that passes validation rather than rejecting the header.
```

### Signature

```typescript
function parseFromHeaderLoose(
  header: string | null | undefined,
): ParsedAddress | null;
```

### Result shape

```typescript
interface ParsedAddress {
  address: string; // lowercased
  name: string | null;
}
```

Returns `null` when the header is `null`/`undefined`, empty after trimming, exceeds the 998-byte limit, or contains no address that passes the same `isEmail` check used by `parseFromHeader`.

### Behavior differences from parseFromHeader

| Case | `parseFromHeader` | `parseFromHeaderLoose` |
|---|---|---|
| Multiple addresses | Rejects (`multiple_addresses`) | Returns the first parseable address |
| Group syntax | Rejects (`group_syntax`) | Flattens into member addresses and returns the first parseable one |
| No valid address | Returns a typed `reason` | Returns `null` |
| Display name | Never returned | Returned when present, otherwise `null` |

<Warning>

Names returned by `parseFromHeaderLoose` can include addressparser's recovery output, trailing tokens or garbage preceding the address. Treat `name` as opaque text for display; sanitize before re-emitting it (e.g. into HTML or a re-sent email).

</Warning>

## Choosing between them

- **Authorization, allowlisting, or any trust decision**: use `parseFromHeader` and branch on `result.ok` / `result.reason`.
- **Showing a sender's name in a UI or log**: use `parseFromHeaderLoose` and treat `name` as untrusted display text.

Both parsers are pure functions with no network or I/O dependency; they operate only on the header string you pass in. For deciding whether an inbound email's sender domain can be trusted (not just whether the header parses), see [Verifying Inbound Email Authenticity](node-sdk-email-authenticity).

## Next steps

<CardGroup cols={2}>

<Card title="Verifying Inbound Email Authenticity" href="node-sdk-email-authenticity">

Use validateEmailAuth and isTrustedSender to anchor authorization decisions to SPF/DKIM/DMARC results and an expected sending domain.

</Card>

<Card title="Parsing Raw Email (.eml)" href="node-sdk-parsing-email">

Parse raw MIME bytes into structured bodies, headers, and attachments, and extract addresses safely from a full message.

</Card>

</CardGroup>
