Fixing Silent Failures in Bluesky's Handle Resolution: When Errors Hide Security Checks

WellKnownHandleResolver was swallowing all non-abort errors, including critical SSRF protection violations, and returning null instead of surfacing the real problem. A fix ensures only genuine network timeouts map to null while security and validation errors bubble up.
What
The WellKnownHandleResolver.resolve() method in atproto was catching every error except AbortError and converting it to null. This meant that when safeFetchWrap blocked a dangerous request (like resolving a handle to 127.0.0.1), the security error got hidden and callers saw a silent null instead of knowing why resolution failed. The fix distinguishes between true network failures (WHATWG fetch TypeErrors, blocked redirects) which legitimately return null, and other errors which must be re-thrown.
Why it matters
Silent failures break debugging and violate the HandleResolver contract. During local development or when security checks trigger, developers had no way to know if a handle failed due to network issues or because something blocked the request. This anti-pattern (CWE-390) masked real problems and made OAuth flows mysteriously fail without diagnostic information.
Who it's for
The atproto maintainers and any developer integrating handle resolution in Bluesky's ecosystem. This affects both server-side and isomorphic environments since the fix uses no external dependencies.
When & where
The issue was discovered in atproto issue #4215 and addressed in draft PR #5102. The fix applies to the @atproto/handle-resolver package, which is foundational to Bluesky's DID and OAuth flows.
How
The solution adds explicit error type checking: only WHATWG fetch TypeErrors (which indicate network problems) and redirect blocks map to null. All other errors, including validation and security exceptions, are re-thrown so callers can handle them appropriately. The package adopted vitest and includes a regression test that confirms the SSRF error surfaces instead of being swallowed.
Takeaway
Error handling matters as much as error throwing. Swallowing exceptions to provide a simpler API surface often backfires when debugging or when security checks need to communicate. By being precise about which errors map to which outcomes, the resolver becomes both more debuggable and more secure.
Draft PR: https://github.com/bluesky-social/atproto/pull/5102
Building an AI agent?
I'm packaging how I ship them into one kit. Early access:
AI Agent Starter Kit →