Store: with POSTULO_PUBLIC_URL unset the application link is a bare path, Paperless refuses it, and an archived document ends as failed #12
Labels
No labels
bug
documentation
enhancement
performance
security
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo-paperless#12
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo-paperless at
973969bb7, followingmetadata.application_urlfrom the core into the custom field and replayingputagainst a mock transport.putwritesmetadata.application_urlinto a Paperless custom field it creates withdata_type="url", guarded only by the value being non-empty. The core builds that value with no request to hand, so on an instance wherePOSTULO_PUBLIC_URLis not set, which is the default, it is a bare path such as/applications/7/. Paperless-ngx validates URL fields and refuses a value with no scheme (uri_validatorinsrc/documents/validators.py, called fromCustomFieldInstanceSerializer.validate, read at v2.18.4). The refusal arrives after the document has been consumed, and it raises out ofput.Where:
src/postulo_paperless/store.py:327-329(PaperlessStore.put:if field_name and metadata.application_url:, thenensure("custom_fields", field_name, data_type="url")andset_custom_field);:198-213(Archive.set_custom_field, which raisesPaperlessErroron any answer but 200);:88-95(_explain, which reads onlydetail). In the core:documents/stores.py:77(application_url = absolute_url(application.get_absolute_url()), no request),notifications/base.py:164-165(return f"{base}{path}" if base else path) andconfig/settings/base.py:530(POSTULO_PUBLIC_URLdefaults to empty).What happens: an operator installs the plugin and leaves
POSTULO_PUBLIC_URLunset; the wiki documents it as optional. A person connects Paperless and keeps the default custom field name, "Postulo". For every document that belongs to an application:PATCHthat writes the link is answered 400, andputraisesPaperlessError: HTTP 400, with no reason, because Paperless's validation errors carry nodetailkey;ExternalRef, and retries: each of the six attempts uploads the whole file again, is told it is a duplicate, and fails at the samePATCH.Checked on the snapshot with a transport that answers the
PATCHas Paperless does: two attempts gave two uploads, one document in the archive andPaperlessError('HTTP 400')both times. Documents with no application have an emptyapplication_urland are archived normally, which makes the failure look random. The tests only ever usehttps://postulo.example/applications/7/.Why it matters: on a default instance every CV and letter sent with an application is shown as failed although the archive holds it, is uploaded six times, and never gets its link into Paperless.
Fix:
urlsplit(value).scheme in ("http", "https")and a host.wait_forhas returned an id the document is archived, so a failure to create or write the custom field should be logged and theExternalRefstill returned; an archived document is then never shown as failed and never uploaded again for it._explainshow Paperless's field errors when there is nodetail, so that a 400 says why.POSTULO_PUBLIC_URL. The core could also leaveapplication_urlempty rather than hand a store a path that leads nowhere from another machine.Done when
tests/test_paperless.pyshowsputwithapplication_url="/applications/7/"returning the document's reference without sending aPATCH.tests/test_paperless.py, with the fake answering thePATCHwith 400, showsputstill returning the reference, so that the copy is recorded as sent and not offered again.Fixed by pull request #18.