Troubleshooting¶
Start with the local facts before retrying a job:
papio daemon status checks the background service without starting it. Most
other commands start the service automatically when they need it. Use
papio jobs get <job-id> to see one job's events and actions, and use
papio batch report <batch-id-or-latest> --markdown to see a research set's
joined outcome.
The Chrome extension is outdated¶
Store-installed extensions update automatically — this section is for unpacked development builds, which can keep running outdated code even after you change the files on disk. The fix is an explicit extension reload; a Chrome restart, killing the process, or reloading the extension's folder alone is not enough.
Preferred UI reload¶
- Open
chrome://extensions. - Turn on Developer mode if it is not already enabled.
- Find papio and use its reload arrow.
- Open the papio details page and confirm its optional host permissions still cover only the publisher sites you intend to use.
- Open the popup or run
papio actions listto give the worker a reason to reconnect.
Automated Chrome reload¶
Prefer papio browser reload: it reloads the connected development-mode
extension from disk and then waits for the new session to reconnect. It
refuses store-installed extensions by design, so use it only with an
unpacked development install. Where local Chrome automation is already
authorized, Chrome's developerPrivate.reload operation for the papio
extension is the programmatic equivalent of the reload arrow and must target
the installed papio extension, not an arbitrary extension.
Last resort: purge the service-worker cache¶
Use this only with Chrome completely closed:
- Close every Chrome process.
- In the affected Chrome profile, remove
Service Worker/ScriptCacheandService Worker/Database(for the default profile these are beneathDefault/Service Worker/). - Reopen Chrome and use the
chrome://extensionsreload procedure above.
This purge also clears service-worker state for web PWAs in that profile; those PWAs re-register on their next use. Do not delete the papio data directory or its database as a substitute for reloading the extension.
Restarting the background service¶
Stopping the background service is explicit:
papio daemon stop returns when the service process has exited. The service
can take up to half a minute to finish its work, and the command then prints
papio: waiting for the daemon (pid N) to finish stopping. The second command
confirms it is stopped without starting it again; the last
command is a normal call and can start a fresh service. On a healthy recovery,
the extension recognizes the fresh service, closes its stale connection,
reconnects, and re-offers your saved browser jobs. It re-offers them within
about two seconds and does not duplicate browser tabs.
If a handoff is still stale after the service is back, reload the extension using
the procedure above, then inspect papio actions list and papio status.
Do not cancel a job merely because its old popup row is stale.
Version mismatches and updates¶
Run papio doctor first when an update or browser integration seems wrong. It
checks papio first, then the pieces it depends on:
PASS access_mode explicit access mode configured
PASS pdftotext Poppler semantic extraction available
...
PASS config parsed /Users/me/.config/papio/config.toml
PASS daemon reachable; version 0.18.0
WARN extension extension has not connected since daemon start
PASS native host (Chrome) manifest allows configured extension
PASS native host (Firefox) manifest allows configured extension
PASS native host (version) matches the daemon (0.18.0)
PASS zotio version 0.16.1; required capabilities available
To update papio, build or install the new version, then stop the running service:
The next command that needs it starts the new service; there is no
papio daemon restart command. If the command-line tool and the service are
different versions, the tool prints a warning. An unknown-field configuration
error means your config was written by a newer papio; install a matching or
newer version before continuing.
The first start after an update can upgrade the database. On a large database
that takes a minute or more: the command prints
papio: upgrading the database; this can take a minute and waits, and other
commands and the browser extension wait for the same upgrade instead of
starting a second service. Let it finish. If it takes longer than ten minutes,
the command stops waiting and says so, and the upgrade carries on in the
background; run the command again later. A database check can also take a
while on a large database, and the command then prints
papio: checking the database; this can take a minute. A command run while
the previous service is still stopping prints
papio: waiting for the previous daemon to stop and starts the new service
once the old one has exited.
The extension popup reports daemon unreachable, papio daemon out of
date, and extension out of date when it needs attention; the toolbar shows
! in those states. When healthy, the popup shows the daemon version, and its
options page shows extension and daemon versions together. Extension updates
arrive through the browser store, while the daemon is updated manually, so an
extension newer than its daemon is the common direction.
Store-installed extensions update automatically. Manually loaded builds from
about:debugging or an unpacked dist/ do not; download the new ZIP from the
release bundle instead. The daemon's extension floor and the popup states flag
an extension that is too old.
Learning about new releases¶
papio never installs updates on its own, and it never contacts a server without being told to. Two mechanisms tell you a newer release exists:
- Through the extension (no network use by papio). Store-delivered
extension updates carry the daemon version they were released with. When the
popup notices the connected daemon is older, its version line changes to
papio <new> is availablewith the upgrade command. papio itself sends nothing anywhere; the browser's normal store update is the only network activity involved. - Opt-in release check. With
check = trueunder[updates]in the configuration (thepapio initprompt offers this, defaulting to yes), the daemon asks the papio and zotio GitHub releases APIs for their latest versions at most once a day each. The requests carry no identifying payload beyond the connection itself, and GitHub already hosts the binaries you would download. Results appear inpapio doctor, in daemon status, and as a single standard-error hint (at most once per day). Configurations without the[updates]section never check.
You are asked to sign in again¶
The browser extension keeps one pinned, muted tab while handoff jobs are still open. If a reload lands on your institution's login page, it stops reloading, brings the tab forward, and flags a sign-in request.
- Open the extension popup and use Focus for the needs-you job, or run
papio actions opento open the current handoff queue. - Sign in on the ordinary Chrome page, including any two-factor or institution step required by that site.
- Return to the provider page. The extension detects your return and resumes.
Do not put credentials in papio configuration, in messages to the extension, or in an MCP tool call. papio is designed to reuse the ordinary browser session, not to automate authentication.
The sign-in page says the request is stale or expired¶
Institutional sign-ins are time-boxed. If login plus a two-factor step takes
long enough, the identity provider may reject the original handoff link with a
"stale request" or "expired" page even though your session is now valid. Sign
in first, then re-run papio actions open. A compatible extension re-drives
the tracked tab through its retained resolver URL when it is on an authentication
page or has been marked sign-in pending; it only focuses a provider page that is
already progressing a download.
The extension recognizes the common OpenAthens/Shibboleth failure pages. When
it does, it brings the work window to the front so the dead page is visible,
records a browser.handoff_failed event on the job (visible in
papio jobs get <id>), and retries the handoff tab on its own. Retries are
capped by the handoff's authentication budget; past it the tab is left on the
failure page for you and the job stays parked.
The resolver opens the wrong paper or an unusable page¶
Inspect papio jobs diagnose <job-id> and papio actions list --json.
For a manual-download action diagnosed as a wrong work, a missing adapter, or
adapter drift, you can ask papio to try the paper's DOI route:
Use the action's current revision from the listing. This requires a connected extension and delegated mode. It keeps the failed action and its evidence, then starts one publisher attempt on the same job. A pending sign-in, challenge, terms prompt, download, or unresolved browser effect blocks the retry. The DOI route may still require institutional access; it does not establish entitlement.
Only one publisher retry is allowed per job. If that route also fails, Open keeps the publisher page available for manual work without starting another download attempt.
Two browsers fight over papio¶
With the extension enabled in more than one browser or profile, only one
browser holds the offer/handoff flow. A browser that is not the holder says so
in its popup — another browser is holding your papio session — and its
toolbar icon shows a grey !. That is not a daemon problem: papio daemon
status answers ok, and the remedy is switching the session, not restarting
anything. Other symptoms: handoff tabs open in the "wrong" browser, or
doctor reports "N other browser(s) waiting".
papio browser sessions # holder + pending, versions, last contact
papio browser use --latest # hand the session to the newest pending browser
papio browser use <session-id> # or pick one explicitly
Quitting the holding browser releases the session immediately; a crashed holder yields within about ten seconds. If you never want a browser to hold the session, disable the papio extension there. Only daemon-initiated offers and handoffs are holder-routed: a waiting browser is acknowledged with its own connection, and anything you initiate yourself — Acquire this page, Send PDF, reading the inbox — still works where you click. Its popup and inbox stay usable; the holder switch only changes where automatic handoffs land. Both surfaces resume by themselves in the browser you claim.
Read doctor output¶
doctor prints stable PASS, WARN, and FAIL rows. Any FAIL makes the
report not OK. The checks below explain the common ones; run papio doctor --json for every check the current build can emit.
| Check | PASS means | WARN or FAIL: what to do |
|---|---|---|
access_mode |
An explicit allowed access mode is configured. | Set access_mode to conservative, assisted, or delegated; papio init creates a conservative profile. |
fetch_policy |
HTTPS-only fetch policy is active. | A warning means fetch.allow_http_loopback is on; disable it outside loopback fixture work. |
data_dir |
The data directory is private and writable. | Correct ownership or permissions so papio can create and write the configured directory. |
retraction |
The latest Retraction Watch fetch succeeded, and the last successful sweep is less than 48 hours old. | SKIP means the source is disabled or no sweep has completed. WARN means the latest fetch failed, the last success is older than 48 hours, or the local status is invalid. Check access to api.labs.crossref.org, keep the daemon running through its next daily maintenance sweep, or follow the row's cache repair instruction. |
config_permissions |
The config is user-only. | A missing config is a warning; create it with papio init. A group/world-readable config is a failure; set it to mode 0600. |
config_ignored_settings |
Only emitted as WARN. |
The config still sets a key this papio no longer supports, such as browser.native_viewer_helper. papio ignores the value. Delete the key from the config file. |
database |
The local database passed its integrity and version checks. | If unavailable in this run, run doctor through the background service. For an integrity failure, restore a verified backup before acquiring more work. |
pdf_worker |
The current papio can run its isolated PDF worker. | Reinstall or rebuild papio and retry doctor. |
pdftotext |
Poppler semantic extraction is available. | Install Poppler; this is a failure. |
pdfinfo |
Poppler's independent page-count check is available. | Install Poppler for the structural cross-check; this is a warning. |
ocr |
OCR dependencies are available when OCR is enabled. | Install Poppler and Tesseract, or explicitly disable OCR. A disabled OCR fallback is a warning because image-only papers need review. |
source_unpaywall |
An enabled Unpaywall source has a contact email. | Set email or disable the source. |
credential:<target> |
Whether the selected OS-store or environment record resolves, without revealing it. Ready means local resolution, not provider authentication. | Run papio config credentials status; repair the binding or unlock the user credential store, then restart the daemon. Missing optional keys use keyless quotas; required-key integrations remain unavailable. |
source_openalex |
An enabled OpenAlex source has email and API key. | Set email; use papio config credentials set sources.openalex for account capacity. |
source_core |
An enabled CORE source has an API credential. | Run papio config credentials set sources.core, or disable the source. |
source_crossref-tdm |
An enabled Crossref TDM source has an API credential. | Run papio config credentials set sources.crossref_tdm, or disable the source. |
credits_<source> |
Today's metered spend is named, with the ceiling and the credentials that ceiling is shared by: openalex has committed 12 of 5000 credits today, from 10000 reported by the provider (shared by anonymous, key-05e71a0b2f38b802). The fuse keeps one row per source per day, so the credential list is what the allowance is shared by, not a per-credential breakdown of the spend. Reported for every source whose daily_credit_fraction is non-zero, healthy or not. |
A warning means today's allowance is spent and that source is parked until 00:00 UTC — papio is waiting, not broken. Lower the reserve by raising daily_credit_fraction, or add capacity on the provider. A warning naming a changed cost schedule means the provider's prices no longer match what papio committed against, and egress stays closed until the new schedule is acknowledged. Before the day's first response arrives the ceiling reads as a conservative cap rather than a fraction of the provider's figure. |
See config-reference.md for exact keys and allowed
values.
zotio-boundary error classes¶
papio stores and prints the following stable, privacy-safe error classes for
zotio-boundary failures. Their hints are sanitized and truncated; use the class
and hint from papio batch report, papio jobs get, or JSON output rather than
copying credentials or filesystem paths into a ticket.
| Error class | Meaning | What to do |
|---|---|---|
zotero_http_4xx |
zotio reported a Zotero HTTP 4xx response. | Check the local zotio/Zotero authorization and the operation shown by the sanitized hint, correct it there, then make and inspect a new zotio plan. |
metadata_unresolved |
zotio found no registry record for the paper, and papio could not describe it from its own record. The hint says why: for example, the DOI that zotio read from the PDF is not the job's DOI. | Compare the hint with the job's identifiers in papio jobs show <job-id>. If an identifier is wrong, submit the paper again with the correct one. Otherwise keep the class and the hint for a bug report. |
zotero_field_validation |
zotio rejected an item field, such as an unknown item field. | Update the incompatible field mapping or compatible zotio version, then create a new plan rather than reusing the failed one. |
mirror_sync_failed |
Synchronizing the zotio mirror failed. | Restore local zotio connectivity and synchronization, then retry planning. |
zotio_exec_timeout |
A zotio command exceeded its deadline. | Confirm the executable works; if the operation legitimately needs more time, set [zotio].timeout_seconds within 5–600 and retry. |
zotio_apply_unconfirmed |
An earlier zotio import ended without a result (a timeout, a shutdown, or a crash), so papio cannot yet tell whether Zotero saved the paper. papio does not repeat the save until it can check. | Wait about two minutes for Zotero desktop to sync, then retry; papio looks the paper up first. It records the paper as filed if the library holds it, attaches the PDF to the item that the earlier save created, or runs the import again if the library has nothing. Keep Zotero desktop open and signed in, since a stalled sync keeps this class on every retry. |
zotio_not_configured |
papio has no usable zotio integration. | Run papio init with the correct --zotio-path, or set [zotio].executable to a usable command. |
plan_confirmation_mismatch |
The supplied confirmation SHA-256 did not match the immutable plan. | Run papio zotio plan again, inspect its preview, and pass that plan's exact SHA-256 to papio zotio apply. |
reservation_conflict |
The apply reservation conflicted or was not finalized. | Let any concurrent apply finish, then make a new plan and apply it; do not force a stale reservation. |
local_db_locked |
A local database was locked. | Let the competing local process finish or close it, then retry the zotio operation. |
network |
Network connection setup failed. | Restore network or DNS/TLS connectivity and repeat the affected operation. |
unknown |
No stable classifier matched the failure. | Inspect the job events and sanitized hint, run papio doctor, and retain the class in any bug report. |
A manual papio zotio plan and papio zotio apply recovery is preview-first:
Do not use a SHA-256 from a different preview, and do not treat an import error as evidence that the validated PDF itself failed.
Browser handoff or review is still parked¶
Use the report reason to choose the next step:
institutional: the ordinary Chrome OpenURL handoff needs the user's institution session.oa_browser: an open-access URL needs the browser after papio's own download did not complete it.terms: the user must read and decide on publisher terms. Without recorded auto-accept consent, papio does not accept them. With explicit persistent consent and a supported accept rule, papio can accept them on your behalf; see the terms row of the user guide.needs_review: inspect the quarantine path in the openverify_identityaction, then explicitly accept or reject it withpapio actions resolve.no_identifier: no DOI, PMID, or arXiv id could be confirmed, so the job settledunavailablerather than opening an institutional handoff. Books, chapters, reports, and theses usually land here — no sign-in makes an identifier-less request fetchable. Find a DOI and re-submit a manual request withpapio acquire --doi <doi>; for a Zotero item, applyzotio --yes items enrich --missing-doiand then re-runpapio acquire --from-zotio.doi_not_registered: the request carries a DOI, but the DOI system has no such handle — it resolves to a "DOI NOT FOUND" page and no link resolver can match it. This is almost always a typo or a mangled copy-paste (one transposed digit is enough). Signing in will not help. Check the DOI on the article's own page and re-submit withpapio acquire --doi <doi>.
The exact report reason is preferable to a blind papio jobs retry; browser and
identity states are intentionally parked for a human decision.
If the report reason does not explain it, ask the job itself:
That names the typed terminal reason, which access tiers the job actually reached,
and any components it holds. The attempted tiers are the useful part when a paper
should have been available: none means no source was ever reached, and a list
containing only open_access when you expected an institutional pass means the
institutional route was never tried rather than tried and refused.
The browser link drops while a page is being captured¶
A handoff or papio adapter capture that ends in browser session disconnected
during page capture means papio's link to the browser dropped while the
extension was sending a captured page back. Jobs bounce straight back to waiting
and the extension reconnects a few seconds later, so it can repeat in a loop.
Large provider pages could trigger this in 0.17.0 and earlier. Upgrade, then confirm the browser connector is the same version as the background service:
The native host (version) check must pass. If it does not, your browser is
still starting an older connector even though the service is current: run
papio native-host install from the papio you want it to use, then quit the
running connector process so the browser starts the new one.
A parked job with no open action¶
A job can sit in awaiting_human after the action it was waiting for is gone —
resolved elsewhere, or cancelled. papio actions list shows nothing to do, so
there is nothing to resolve, and a retry is the wrong instrument because the job
is parked rather than failed. The background sweep clears these on its own
schedule; to clear one immediately:
It returns the job to resolving and reports what it found, so an outcome of
not_parked or has_open_actions means the job is not actually orphaned and the
parked state is genuine — resolve the open action instead. It is deliberately
narrow: it will not reopen a job that is legitimately waiting on you.
A handoff has been inactive for more than seven days¶
After a week, an open human action is no longer offered automatically when a session goes live, and the reminder schedule stops raising it. This prevents one uncompleted handoff from creating repeated tabs and notifications.
This does not expire the action. It remains open, listed by papio actions
list, and available through papio actions open. That command opens the
handoff explicitly.
WARN quiesced_actions 3 human action(s) have gone quiet after waiting more than 7 days; oldest opened 40 days ago
fix: papio no longer offers these on its own — run 'papio actions open' to
drive them, or 'papio actions dismiss' to clear the ones you are done with
If a handoff becomes inactive again, the library may not hold the title, the
provider may have changed its login, or the DOI may be wrong. Check
papio jobs receipt <job-id> before reopening it.
The paced drive opens nothing¶
Ask the drive what blocks it:
The blocked by: list names each reason; the
user guide explains
every name. The common cases:
disabled:[drive] enabled = trueis missing, or the background service has not restarted since you added it. Runpapio daemon stop; the next command starts it with the new setting.paused: you paused it, or the sign-ins for two papers from different providers at the same library did not come back. Sign in to your library in the browser; the drive continues when any sign-in returns. Or runpapio drive resume.holder_absent: no browser is connected. Open the browser that has the extension;papio browser sessionsshows which browser receives handoffs.no_eligible_job: nothing parked can be opened now. The drive leaves a paper alone for 6 hours after it opens it, skips providers that refuse this browser, and never opens anopenurl_availableadvisory.
papio closed a tab you wanted¶
From extension 0.15.0, papio closes the tab of a paper once it filed the paper or the job ended, including a PDF tab. For a filed paper, choose Reopen in the toast to get the tab back, or open the history page. To keep a tab in future, pin it or move it out of papio's work window or tab group before papio is finished with it. Looking at a tab only delays the close. See When papio closes its tabs.