Skip to content

Fix doc.file() throwing when the same in-memory attachment is added twice - #1781

Merged
blikblum merged 1 commit into
foliojs:masterfrom
MahathirMohammadShuvo:fix-duplicate-in-memory-attachment
Aug 25, 2026
Merged

Fix doc.file() throwing when the same in-memory attachment is added twice#1781
blikblum merged 1 commit into
foliojs:masterfrom
MahathirMohammadShuvo:fix-duplicate-in-memory-attachment

Conversation

@MahathirMohammadShuvo

Copy link
Copy Markdown
Contributor

doc.file() reuses an existing reference when the same attachment is embedded
twice, comparing metadata in isEqual(). That comparison dereferenced
Params.CreationDate unguarded, and the ModDate comparison below it covered
only the case where both sides were absent — so an asymmetric absence threw
there as well.

CreationDate / ModDate are set only in the fs.statSync(src) branch, so a
source that is not read from disk — an ArrayBuffer, a Uint8Array or a data
URL — has neither:

const buffer = Buffer.from('example text');
doc.file(buffer, { name: 'file.txt' });
doc.file(buffer, { name: 'file.txt' });
// TypeError: Cannot read properties of undefined (reading 'getTime')

Where it came from

The unguarded .getTime() comparison arrived in #1544, which fixed a real bug —
the dates were being compared by identity, so two equal dates never matched.
bcc64c2 later added a guard to the ModDate line for the case where both
sides are absent, which is why only CreationDate looks obviously unguarded
today.

The fix

Both dates now go through one helper. Two absent dates match; an absent date
never matches a present one.

Asymmetric absence returning false is the correct answer rather than merely
the safe one: the dates are written into the EmbeddedFile stream dictionary, so
reusing the first reference would silently discard the creationDate the second
caller passed.

No non-throwing path changes result — the only behaviour that moves is the one
that previously threw.

Tests

3 added, all 3 fail on master, at three distinct positions in isEqual (both
CreationDate operands and the ModDate one). They discriminate rather than
just proving "no throw": the dedup case asserts both filespecs point at the same
/F reference, and the two asymmetric cases assert that two separate streams are
emitted.

Unit suite 431/431.

…wice

The deduplication check dereferenced Params.CreationDate unguarded, and the
ModDate comparison below it covered only the case where both sides were absent,
so an asymmetric absence threw there as well. A source that is not read from
disk - an ArrayBuffer, a Uint8Array or a data URL - carries no file system
timestamps, so embedding one twice under the same name threw TypeError instead
of reusing the existing reference.
@blikblum
blikblum merged commit 69671d9 into foliojs:master Aug 25, 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