Skip to content

fedify lookup should use UrlError.reason to explain DNS failures #1064

Description

@dahlia

Background

Since 2.1.0, fedify lookup validates URLs with validatePublicUrl() unless -p/--allow-private-address is given. When an error reaches the lookup command's error handler, getLookupFailureHint() in packages/cli/src/lookup.ts picks the hint to print. It recognizes private-address rejections by matching the error message (“invalid or private address”, “localhost is not allowed”) and treats everything else as a possible authorization failure. A DNS failure is also a UrlError, but its message doesn't match, so the user is told to try -a/--authorized-fetch.

#1060 added UrlError.reason to 2.0-maintenance, which tells DNS failures ("dns") apart from disallowed URLs ("disallowed"), and #1062 makes the library's own callers use it. Neither has been merged forward into 2.1-maintenance yet. Once they are, the CLI can use reason as well.

Expected behavior

  • getLookupFailureHint() returns a separate hint for UrlError with reason === "dns", one that points at the hostname or the network instead of at authorized fetch.
  • For a UrlError, isPrivateAddressError() requires reason === "disallowed" and keeps the message check on top of it. The message check can't go: Unsupported protocol is also "disallowed" but isn't a private address, and lookup.test.ts already tests that it doesn't get the private-address hint.
  • Tests cover both reasons, using new UrlError(…, { reason: "dns" }) directly instead of relying on real DNS.

This only covers errors that reach getLookupFailureHint(). The plain single-object lookup never does, because lookupObject() swallows the loader's error and returns null; that path is tracked in #1063. If the fix for #1063 keeps the loader's error, the same reason check should apply to that path.

Where to work

Branch off 2.1-maintenance after #1062 has been merged forward into it. The change will then be merged into 2.2-maintenance, 2.3-maintenance, and main. The hint code on main has diverged a little (describeError(), getPrivateUrlCandidate()), so expect a small conflict there.

AI disclosure

This issue was drafted with Claude Code (Claude Opus 5.5), with a style review by Codex, and reviewed and edited by a human before submission.

Metadata

Metadata

Assignees

Type

Fields

Priority

None yet

Effort

None yet

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions