mailto: and the Problem with Silent Failure

About a 32-year-old URI scheme, and why good engineering entails selecting how things break.

In systems engineering, there is a principle that is often overlooked: when something fails, it should be evident. A request that simply vanishes is worse than a 500 error. Undefined behavior is worse than a compile error. A timeout is better than a hang. It's much more difficult to determine what's wrong when you can't see it, but you can fix what you can see. ( Kinda why a lot of vibe coded apps fail )

mailto: is a case study in such behavior. A case study of this kind of behavior is mailto. It has been in HTML since 1994, is technically 'fine', passes all linters, but for a significant portion of users, nothing happens and there is no sign that something went wrong when it is referenced by the browser.

I want to discuss why that occurs, why AI continues to produce it, and what the alternative looks like when you reason from what Aristotle called archai, first principles. 1

What mailto: Actually Is

mailto: is not an HTML tag. It is an IETF-defined URI scheme, similar to https: or ftp:. It was formalized in RFC 1738 in December 1994, expanded in RFC 2368 (July 1998), and received its final revision in RFC 6068 (October 2010).2

Most people are unaware of how much the scheme can do. The subject, body, CC, and BCC can all be pre-filled and encoded into the URL:

<a href="mailto:you@example.com?subject=Quick%20Question&body=Hey%2C%20I%20saw%20your%20site..."> Email us </a>

An anchor tag can contain basically an entire email draft.

Opaque Delegation

A browser does not process the request itself when it comes across mailto:. It transfers control to the operating system, which searches for the program that has been designated as the mailto: protocol's default handler. Said program opens if it is installed and set up but if not, the url header is displayed on a blank page and no redirect occurs.

Using this scheme opens up a black box that the developer has no access to. In this black box are a series of systems such as the OS default app settings, the protocol handler registry in the browser, and the status of any installed email client. Has the email client opened? Was the message sent by the user? Did they come to a standstill? You have no idea.

However, in 1994, this transfer of control was pretty sure fire since most people had a pre-configured email client. But about a decade later, Gmail launched in 2004, and with it emails moved into the browser. But browsers cannot handle mailto: innately since it relies on OS-level protocol registrations. If the user does all their email in a Chrome tab and never configured a handler, clicking mailto: either opens an Apple Mail setup wizard, triggers a Windows Store prompt, or does nothing.

Even on macOS, where Apple Mail ships pre-installed, the UX is pretty bad when it comes to changing the default mail handler. To change the default mail handler, you have to open Mail, go to Settings, and pick a different app from the dropdown. Except the Settings menu is greyed out unless you have already configured an email account in Mail. To tell macOS you do not want to use Apple Mail, you first have to set up Apple Mail.3 The terminal workaround that power users relied on (LSSetDefaultHandlerForURLScheme) was deprecated in macOS 14.2. This has not changed through Sequoia or Tahoe. In addition to this, Chrome can be set as the default mail handler, and it cannot handle mailto: links. Apple apparently considers this acceptable.

There is no onerror callback for a mailto: click so there is no programmatic way to determine whether it succeeded. In any other context we would call this an unobservable failure mode and treat it as a bug.

Exposed by Design

Another problem with mailto: links is that they publish your email address in the DOM in a format specifically designed to be machine-readable. The href says mailto:you@example.com. Bots have been harvesting these with trivial regex(es?i?) since the late '90s.

In its security considerations, RFC 6068 recognizes the fact that mailto constructs can be located within HTML pages by automated means (even bcc'ed ones) and that addresses harvested this way are likely to end up on spam lists. The spec even acknowledges that sophisticated bots can get around obfuscation, which is a mentioned defence.4 An RFC hedging against a flaw in its pretty noteworthy.

You can do a bit of obfuscation by encoding characters as HTML entities, reconstructing the address in JavaScript at runtime, and splitting it across data attributes. Some of these reduce harvest rates but they are workarounds for a property of the scheme itself. It was built to be machine-readable. That is a feature for email clients and a vulnerability for developers.

Why AI Keeps Generating It

Ask a language model to build a contact page and there is a strong chance you get mailto:.

LLMs learn to write code by reading code, and mailto: is heavily present within the training corpus. It appears in every introductory HTML tutorial, in MDN docs, in Stack Overflow answers, in W3Schools, in the source of millions of archived websites. If you estimated the probability distribution over HTML patterns following the phrase "contact us," mailto: is extremely likely to follow. It has been the canonical answer to that question for 30 years. And this is one of the instances where the most probable solution doesn't make the cut and LLM's will fall short.

There is also a bias toward 'simpler' solutions such as mailto: which is a singular line of code. A language model gravitating toward the simplest complete answer will pick the mailto: token whenever it pops up. And the output is technically correct as it is valid HTML and by all formal metrics available in the training data, it is good code. This is not unique to mailto:. You see the same dynamic in AI-generated code that reaches for deprecated APIs or patterns that were best practice five years ago

Choosing How Things Break

There is no single HTML construct that opens an email compose window in every user's preferred client. Every approach to contact on the web makes an assumption about the user's environment. The engineering question is not which assumption is universally right, but what happens when the assumption is wrong.

mailto: has no ways to poll for the outcome. A direct compose link, like Gmail's mail.google.com/mail/?view=cm&fs=1&to=you@example.com, is a standard https:// URL which will mostly load and if not, it will show an error. It assumes the user has Gmail, which initially appears to be a very broad (but pretty probable)assumption. But at least when that assumption is wrong, the user lands on a login page and they see what happened. They can go back, or log in, or try something else. The failure is explicit.

A plain-text email address on a webpage makes no assumptions. The user has the ability to view, copy, and paste it into any application. It is incredibly portable but the user bears the expense of doing the work themselves.

On this very site, I link the words "reach out" to a Gmail compose URL and display my email address as visible, copyable text. The link cannot silently fail. And anyone who does not use Gmail can see the address and handle it however they prefer. Every instance mailto: fumbles is covered by these two HTML tags.

reach out

This is not a solution that works every time. Rather, it is a conscious decision about which assumption to make and how to respond when that assumption is violated. And that is what engineering actually is: not only building things that never fail, but building things that fail in ways you can see, reason about, and overcome.

mailto: fails in ways you cannot see which is why I do not use it, and why it is the first thing I check when an AI builds a contact page.

Feel free to "reach out" with any thoughts or questions about this essay, they will be very appreciated

1. Aristotle, Physics I.1 (184a10): "We do not think that we know a thing until we are acquainted with its primary conditions or first principles, and have carried our analysis as far as its simplest elements." The term archai recurs throughout the Metaphysics (981b28) as the starting points from which all other knowledge is derived.

2. RFC 1738 defined the basic mailto: syntax. RFC 2368 added query parameters for subject, body, CC, and BCC. RFC 6068, the current standard, added internationalization and IRI compatibility. Jamie Zawinski is a named author on both RFC 2368 and RFC 6068.

3. LSSetDefaultHandlerForURLScheme in Apple's LaunchServices framework was deprecated in macOS 14.2.1 (Sonoma). As of Tahoe 26.0, the only supported method is Mail.app's Settings panel, which requires an active account configured in Mail. This Catch-22 has persisted unchanged since at least macOS Ventura.

4. See Section 7 ("Security Considerations") of RFC 6068.