Skip to content

Throw from addNamedEmbeddedFile when no ref is given - #1772

Merged
blikblum merged 1 commit into
foliojs:masterfrom
MahathirMohammadShuvo:throw-on-missing-embedded-file-ref
Aug 22, 2026
Merged

Throw from addNamedEmbeddedFile when no ref is given#1772
blikblum merged 1 commit into
foliojs:masterfrom
MahathirMohammadShuvo:throw-on-missing-embedded-file-ref

Conversation

@MahathirMohammadShuvo

Copy link
Copy Markdown
Contributor

Follow-up to #1771, as you asked — the throw on its own. lib/tree.js is
untouched, so the PDFTree.toString filter you were less inclined to merge is
not in here.

The bug

doc.addNamedEmbeddedFile('phantom.txt', undefined);
/EmbeddedFiles <<
  /Names [
    (phantom.txt) undefined
]
>>

undefined is not a PDF object, so the document does not parse. The caller asked
for an attachment under a name and got a broken file with no signal.

Why throw, of the three you listed

doc.file() already refuses the same thing:

doc.file(undefined, { name: 'x.txt' });    // Error: No src specified
doc.addNamedEmbeddedFile('x', undefined);  // silently accepted, corrupt output

addNamedEmbeddedFile is the lower-level route to the same place, so the two now
agree. Silent drop is the option I would argue against hardest — a valid PDF with
nothing attached and no error is harder to debug than a stack trace.

In addNamedEmbeddedFile rather than PDFTree.add, because add is shared with
/Dests, /JavaScript and ParentTree, which only ever receive values the
library builds itself.

Scope

!ref rather than === undefined, matching the if (!src) guard in file().
That also catches null, which is the more awkward of the two: it serialises to
the PDF null keyword, so the file parses and you get a silently empty
attachment rather than a loud broken one. Happy to narrow it if you would rather.

The guard sits before the lazy /EmbeddedFiles init, so a rejected call does not
leave an empty name tree behind in the catalog.

name is not validated. A bad key serialises to (undefined), which parses, and
the same applies to addNamedDestination and addNamedJavaScript — fixing it
here alone would make the three siblings inconsistent. Separate change.

The one caller inside the library, lib/mixins/attachments.js:102, passes a
filespec file() has just created, and file() throws before it can return
anything falsy — so the library's own path cannot reach the new guard.

Marked breaking, though anyone hitting this today is shipping a PDF that does not
parse.

Tests

2 added. throws when the ref is missing fails on master. The second registers a
filespec created with hidden: true and passes either way — a guard that the
normal hand-registration path still works, not evidence for the change.

Unit suite 396/396.

Calling it without a ref wrote the literal token `undefined` into the
/EmbeddedFiles name tree, producing a PDF that does not parse. doc.file()
already rejects a missing src; this makes the lower-level route agree.
@blikblum
blikblum merged commit bc6667e into foliojs:master Aug 22, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants