Skip to content

Troubleshooting

Start with the local facts before retrying a job:

papio doctor
papio daemon status
papio status
papio actions list

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

  1. Open chrome://extensions.
  2. Turn on Developer mode if it is not already enabled.
  3. Find papio and use its reload arrow.
  4. Open the papio details page and confirm its optional host permissions still cover only the publisher sites you intend to use.
  5. Open the popup or run papio actions list to give the worker a reason to reconnect.

Automated Chrome reload

Where local Chrome automation is already authorized, use Chrome's developerPrivate.reload operation for the papio extension. This is the programmatic equivalent of the reload arrow; it is not a papio CLI command 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:

  1. Close every Chrome process.
  2. In the affected Chrome profile, remove Service Worker/ScriptCache and Service Worker/Database (for the default profile these are beneath Default/Service Worker/).
  3. Reopen Chrome and use the chrome://extensions reload 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
papio daemon status
papio status

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:

papio daemon stop

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 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 available with 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 = true under [updates] in the configuration (the papio init prompt 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 in papio 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.

  1. Open the extension popup and use Focus for the needs-you job, or run papio actions open to open the current handoff queue.
  2. Sign in on the ordinary Chrome page, including any two-factor or institution step required by that site.
  3. 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.

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. Until a waiting browser is given the session, its popup and inbox only report which browser has it: the daemon negotiates page acquisition and the inbox with the browser it acknowledged, and it acknowledges one at a time. 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 every check the command 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.
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.
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.
source_openalex An enabled OpenAlex source has email and API key. Set email and sources.openalex.api_key, or disable the source.
source_core An enabled CORE source has an API credential. Configure sources.core.api_key, or disable the source.
source_crossref-tdm An enabled Crossref TDM source has an API credential. Configure sources.crossref_tdm.api_key, 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 UTCpapio 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.
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_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:

papio zotio plan <job-id>
papio zotio apply <plan-id> --confirm-sha256 <exact-sha256>

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; papio does not accept them.
  • needs_review: inspect the quarantine path in the open verify_identity action, then explicitly accept or reject it with papio actions resolve.
  • no_identifier: no DOI, PMID, or arXiv id could be confirmed, so the job settled unavailable rather 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 with papio acquire --doi <doi>; for a Zotero item, apply zotio --yes items enrich --missing-doi and then re-run papio 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 with papio 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:

papio jobs receipt <job-id>

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.

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:

papio doctor
papio native-host status

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:

papio jobs repair-awaiting-human <job-id>

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.