Skip to content

feat(cli): the found-nothing path exits 0 with no trace — report which sources ran, the command that widens the search, and which source is most likely to hold it #505

Description

@sotashimozono

The quiet failure is the one with no diagnostics, and it exits 0

Measured on 0.8.9, two paths for the same command:

outcome exit trace rows
found but blocked — error[CAPABILITY_DENIED] 3 6 (consulted: … / not consulted (an earlier source answered))
found nothing — metadata-only: no OA PDF available 0 0
$ doiget fetch 10.1137/0117004 ; echo "EXIT=$?"
fetched doi:10.1137/0117004 (metadata-only: no OA PDF available) -> …/doi_10.1137_0117004.toml
     "On the Continuity of the Generalized Inverse" by Stewart, G. W. (1969)  [crossref/closed]
EXIT=0

That line is byte-identical whether the six optional sources were on and had nothing or off and
never asked
. I checked both: the found-nothing path prints no trace in either case.

Which is exactly the distinction #413 built the trace for —

"We asked and it had nothing" and "we never asked" are different problems with different fixes, and
were previously the same observable.

— absent on the path where it is most misleading, because that path is not an error and so has no
error[…] block to hang the trace on. #445 extended the trace from NOT_FOUND to the blocked
content leg; this is the third case, and the only one that reads as a result.

Why it matters more than the exit code suggests. A user searching for a paper reads
no OA PDF available as "this paper has no OA copy". With the default profile that sentence means
only "Crossref, Unpaywall and arXiv have nothing" — three of eleven sources. I concluded twice today
that a paper was unreachable on the strength of it, and had to redo the search. A narrow default is
the right design (#413: "the default binary is narrow, rate-capped, provenance-logged and
ToS-clean"
); a narrow default that does not say it is narrow is a different thing.

What the failure should return

Three parts, in this order.

1. What was actually consulted this run

The rows already exist on the other two paths. Emit them here too, and make the disabled ones
visible rather than absent — an omitted row and a row saying "off" are not the same message:

no OA copy found for 10.1137/0117004.
  consulted:
    crossref     answered (metadata; oa_status = closed)
    unpaywall    answered: no OA location
    arxiv        answered: no preprint
  not consulted (disabled):
    datacite  doaj  europe-pmc  hal  openaire  core  openalex

2. The command that widens it

Not prose — the line to paste. #478 is the precedent to follow, and its complaint applies here too:
the machine path already computes which flag actually covers the case while the human path prints a
generic suggestion. Here the exact set is known, so:

  to widen the search:
    DOIGET_ENABLE_OPENALEX=1 DOIGET_ENABLE_CORE=1 DOIGET_ENABLE_OPENAIRE=1 \
    DOIGET_ENABLE_HAL=1 DOIGET_ENABLE_EUROPE_PMC=1 DOIGET_ENABLE_DATACITE=1 \
      doiget fetch 10.1137/0117004

with the two that carry an extra prerequisite named as such — CORE (DOIGET_CORE_API_KEY, free)
and OPENALEX (needs --features citation) — so a user does not enable one and silently get a
degraded answer.

#470 is the blocker for doing this per-row: classify_attempt throws the DenialContext away, so an
attempt row cannot carry its own remediation today.

3. Which one is most likely — ranked, never filtered

This is the part that does not exist yet, and the one worth being careful about: a ranking that is
wrong is worse than no ranking, because it makes people stop early.
So it must be an ordering of
the full list, never a shortlist, and it must name the signal it ranked on.

The strong case: OpenAlex turns the guess into a lookup. It reports locations directly, and on
the three DOIs I tested today it separates the two situations cleanly:

DOI OpenAlex locations truth
10.1098/rspa.2014.0585 5 — incl. Europe PMC PMC4277194 and two Oxford ORA records free PDF really is at Europe PMC (670 kB, fetched)
10.1007/bf02551288 1 — publisher landing page only nothing anywhere
10.1137/0117004 1 — publisher landing page only nothing anywhere

3/3, and n=3 — not a validated heuristic, but the mechanism is not statistical: OpenAlex is listing
the hosts, so with it enabled the answer is "Europe PMC has it", not "Europe PMC might". That makes
DOIGET_ENABLE_OPENALEX the first thing to suggest, and it is cheap: one metadata call, no
retrieval. (Consuming those locations to actually fetch is #461; this issue only asks to report
them.)

The fallback, when OpenAlex is off: rank from metadata already in hand after the Crossref leg —
no extra network. Signals, strongest first:

  • DOI prefix → registrant. doiget already maintains a prefix→publisher map for Tier-3 scoping
    (ADR-0041); the same map says whether an enabled-able TDM source covers this prefix at all.
  • venue / subject → Europe PMC for anything PMC indexes (and note it indexes well beyond
    biomedical — the Royal Society paper above is in it).
  • author affiliation / funder → HAL for a French institution, OpenAIRE for an EU funder.
  • CORE last, always present. Its own module doc calls it "the broadest single OA index outside
    Unpaywall and therefore the LAST fallback in the chain" — so it is never the top suggestion and
    never absent from the list.

Rendered:

  most likely to hold it, given "SIAM J. Appl. Math., 1969, prefix 10.1137":
    1. openalex   lists every known location — turns this ranking into a lookup
    2. core       broadest cross-repository index (needs DOIGET_CORE_API_KEY, free)
    3. openaire   European repository aggregation
    …and 4 more, above.
  ranked on: venue, DOI prefix, year. This is a guess, not a report — 1. is not.

That last line matters. Items 2+ are heuristics over metadata; item 1 is a claim about what an index
says. Presenting them in one list without marking which is which would be the failure mode this
whole issue is about.

Scope

Reporting only — no new retrieval, no new source, no change to what gets tried. Adjacent and
distinct: #461 (actually try the repository copies), #470 (the remediation plumbing this needs),
#478 (compute the precise suggestion instead of a generic one), #497 (write down the ceiling that
now holds).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions