Skip to content

fix: available domains were always reported as taken - #2

Merged
FlexKleks merged 3 commits into
mainfrom
fix/rdap-url-and-availability-detection
Oct 28, 2026
Merged

FlexKleks merged 3 commits into
mainfrom
fix/rdap-url-and-availability-detection

Conversation

@FlexKleks

Copy link
Copy Markdown
Owner

Checking an unregistered domain always returned "taken". Four separate defects contributed to this, all of which had to be fixed for an available domain to be detected correctly.

  1. Duplicated /domain/ in RDAP URLs

    Most configured endpoints already end with /domain/, but the path segment was appended unconditionally, producing URLs such as https://rdap.verisign.com/com/v1/domain/domain/google.com which registries answer with HTTP 400. _build_query_url() now appends the segment only when the endpoint does not already end with it. Verified against all 193 entries of the TLD registry.

  2. HTTP 404 with an empty body treated as an error

    A 404 was only accepted as "not found" if its body contained one of a few text markers. Verisign answers 404 with Content-Length: 0, so those responses became RDAPStatus.ERROR and therefore "taken". Per RFC 7480 Section 5.3 the status code is the defined signal and the error body is optional, so the status code is now authoritative. A 404 is only rejected if the body carries an RDAP error object whose errorCode contradicts it.

  3. WHOIS "no match" signals that never matched

    The .com/.net signal was "No match for domain", while Verisign actually replies 'No match for "EXAMPLE.COM".'. The .org, .io and .biz signals were wrong too, so WHOIS confirmation returned AMBIGUOUS and the conservative logic fell back to "taken". Since .com has no secondary RDAP endpoint configured, WHOIS is the only confirming source, which is why fixing the URL alone was not enough. Signals corrected against the live registry responses.

  4. Misleading confidence for "taken"

    Every "taken" result reported high confidence, including results that came only from errors, timeouts or missing confirmation. high is now reserved for results backed by an actual registration signal (RDAP FOUND or WHOIS FOUND); conservative fallbacks report low.

Also fixed: unclassifiable 404 responses are no longer retried. They previously fell through to the generic server_error branch, causing four identical requests per domain against the registry. And version was stuck at 0.1.0 while the package version was already 0.2.0, so --version reported the wrong release.

The decision logic itself is unchanged: AVAILABLE still requires multi-source confirmation and any uncertainty still yields TAKEN.

Verified end-to-end against the live registries:
google.com -> taken / high (rdap_primary=found)
hfupwefbabfdskf.com -> available / high (rdap_primary=not_found,
whois=not_found)
Both with 0 retries instead of 4 requests each. All 114 tests pass.

Bumps version to 0.2.1.

Fixes #1
Reported-by: @rkahv

Checking an unregistered domain always returned "taken". Four separate
defects contributed to this, all of which had to be fixed for an
available domain to be detected correctly.

1. Duplicated /domain/ in RDAP URLs

   Most configured endpoints already end with /domain/, but the path
   segment was appended unconditionally, producing URLs such as
   https://rdap.verisign.com/com/v1/domain/domain/google.com which
   registries answer with HTTP 400. _build_query_url() now appends the
   segment only when the endpoint does not already end with it.
   Verified against all 193 entries of the TLD registry.

2. HTTP 404 with an empty body treated as an error

   A 404 was only accepted as "not found" if its body contained one of
   a few text markers. Verisign answers 404 with Content-Length: 0, so
   those responses became RDAPStatus.ERROR and therefore "taken". Per
   RFC 7480 Section 5.3 the status code is the defined signal and the
   error body is optional, so the status code is now authoritative. A
   404 is only rejected if the body carries an RDAP error object whose
   errorCode contradicts it.

3. WHOIS "no match" signals that never matched

   The .com/.net signal was "No match for domain", while Verisign
   actually replies 'No match for "EXAMPLE.COM".'. The .org, .io and
   .biz signals were wrong too, so WHOIS confirmation returned
   AMBIGUOUS and the conservative logic fell back to "taken". Since
   .com has no secondary RDAP endpoint configured, WHOIS is the only
   confirming source, which is why fixing the URL alone was not
   enough. Signals corrected against the live registry responses.

4. Misleading confidence for "taken"

   Every "taken" result reported high confidence, including results
   that came only from errors, timeouts or missing confirmation. high
   is now reserved for results backed by an actual registration signal
   (RDAP FOUND or WHOIS FOUND); conservative fallbacks report low.

Also fixed: unclassifiable 404 responses are no longer retried. They
previously fell through to the generic server_error branch, causing
four identical requests per domain against the registry. And
__version__ was stuck at 0.1.0 while the package version was already
0.2.0, so --version reported the wrong release.

The decision logic itself is unchanged: AVAILABLE still requires
multi-source confirmation and any uncertainty still yields TAKEN.

Verified end-to-end against the live registries:
  google.com          -> taken     / high  (rdap_primary=found)
  hfupwefbabfdskf.com -> available / high  (rdap_primary=not_found,
                                            whois=not_found)
Both with 0 retries instead of 4 requests each. All 114 tests pass.

Bumps version to 0.2.1.

Fixes #1
Reported-by: @rkahv
@FlexKleks FlexKleks self-assigned this Oct 28, 2026
json.loads() on a str can only raise JSONDecodeError (malformed JSON) or
TypeError (wrong input type), so catching those explicitly is more precise
than a blind except Exception.

Also keeps the ruff findings for src/ at the same count as main (170), so
this change does not add to the pre-existing lint debt.
The lint job started failing with 170 findings in src/ without any code
change. Root cause: the project has no [tool.ruff] configuration, so ruff
applies its own implicit default rule set, and the workflow installs ruff
unpinned. Ruff expanded those defaults between releases.

Verified with an identical sample file:
  ruff 0.8.6  (what CI resolved to in Dec 2025) -> All checks passed
  ruff 0.17.3 (what CI resolves to today)       -> UP045, UP045, BLE001

So the job was green on main in December and is red now purely because of
an upstream release.

Two changes make the job deterministic again:
- pyproject.toml declares select = ["E4", "E7", "E9", "F"], the rule set
  the project was developed and kept green against, so the rule set no
  longer depends on ruff's shifting defaults.
- the workflow pins ruff==0.17.3 instead of resolving the latest release
  on every run.

ruff check src/ now passes with 0 findings.

None of the 170 suppressed findings were correctness bugs. The bulk was
style modernization: 98x Optional[X] -> X | None, 21x blind except, 8x
import sorting, 8x unnecessary pass, 7x datetime.UTC alias, plus smaller
counts of deprecated-alias and simplification rules.

Two are worth addressing separately on their own merits, not as drive-by
changes in a bugfix PR:
- ASYNC230: cli.py opens files with blocking open() inside async functions
- S110: scheduler.py swallows task exceptions without logging them

Adopting the broader rule set is a deliberate project decision and belongs
in its own PR, where the ~170 mechanical edits can be reviewed on their own
instead of burying the RDAP fix.
@FlexKleks
FlexKleks merged commit 3dbcb29 into main Oct 28, 2026
7 checks passed
@FlexKleks
FlexKleks deleted the fix/rdap-url-and-availability-detection branch October 28, 2026 21:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] RDAP URL Construction Bug and Incorrect Taken Status with High Confidence

1 participant