Store: a retried document is matched to the wrong Paperless document when its title holds a '#' and a number #11
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#11
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, reading how the duplicate answer is parsed and replaying it against a mock transport that answers in Paperless-ngx's own words.When Paperless refuses a file it already holds, the plugin reads the existing document's id out of the task's message with
_DUPLICATE_ID = re.compile(r"#(\d+)")and.search(), which takes the first match. Paperless-ngx 2.x writes that message as<file name>: Not consuming <file name>: It is a duplicate of <title> (#<id>).(src/documents/consumer.py,pre_check_duplicateand_fail, read at v2.18.4), so the title comes before the id. The title is the one this plugin composed from the kind, the company and the job title, or an upload's own title, and any#<digits>in it wins. The plugin then treats that number as the document. The retry the README calls safe is not safe for such a title.src/postulo_paperless/store.py:56(_DUPLICATE_ID);:182-187(Archive.wait_for, theFAILUREbranch:match = _DUPLICATE_ID.search(result)andreturn int(match.group(1)));:177-179(the same search in theSUCCESSbranch);:326-329(PaperlessStore.put, which writes the application's link into the document that id names) and:330-334(theExternalRefbuilt from it).tests/test_paperless.py:82-83(the fake's message, the same shape) and:296(the only duplicate test, whose title holds no#).WAIT_SECONDSto consume it,putraisesStillConsuming, and the scheduler offers the file again. Paperless answers that it is a duplicate of "CV — Company — Engineer (Req #48213) — date (#57)". Checked on the snapshot:putraisesPaperlessError: HTTP 404. Every one of the six attempts uploads the file, is told it is a duplicate and fails the same way, so the copy ends failed although the archive holds it.putreturnedref.id == "1": document 1, an unrelated document, had its Postulo link replaced with this application's address, and Postulo recorded and linked document 1 as the copy.(#<digits>), allowing the full stop and the "Note: existing document is in the trash." sentence that may follow it, or use the last match. Apply the same to theSUCCESSbranch. Worth knowing while there: on Paperless-ngx's development branch, as read on 2026-10-01,pre_check_duplicaterefuses a duplicate only whenCONSUMER_DELETE_DUPLICATESis set and otherwise consumes the file again, so the premise that a second offer is refused needs checking against the version that ships it.Done when
tests/test_paperless.pyoffers the same file twice with a role of "Engineer (Req #1)" while document 1 exists, and shows the secondputreturning the id of the document Paperless named, with document 1's custom fields untouched.tests/test_paperless.pydoes the same for an upload whose own title holds#and a number, with the fake's message in Paperless's full form (file name twice, a trailing full stop).Fixed by pull request #18.