Summary
XML::Document.parse and HTML4::Document.parse open Pathname inputs but do not close the resulting IO. Repeated parsing retains descriptors until garbage collection.
Reproduction
path = Pathname('/tmp/input.xml')
path.write('<root/>')
32.times { Nokogiri::XML::Document.parse(path) }
On a system with /dev/fd, the open descriptor count grows by 32 before GC. HTML4 has the same behavior.
Candidate and verification
Use block-form Pathname#open and parse inside the block. Source-only candidate 277beb48391b57e3faab2a3144fc2786e8985854 is based on current main. Current passes 2,841 runs /10,329 assertions with 24 skips. Focused XML/HTML4 models show zero descriptor growth and preserve URL/encoding/options behavior; release leaks one descriptor per parse.
This is issue-first because Nokogiri requires behavioral tests/changelog changes and the consumer audit does not modify upstream tests.
Summary
XML::Document.parseandHTML4::Document.parseopenPathnameinputs but do not close the resulting IO. Repeated parsing retains descriptors until garbage collection.Reproduction
On a system with
/dev/fd, the open descriptor count grows by 32 before GC. HTML4 has the same behavior.Candidate and verification
Use block-form
Pathname#openand parse inside the block. Source-only candidate277beb48391b57e3faab2a3144fc2786e8985854is based on current main. Current passes 2,841 runs /10,329 assertions with 24 skips. Focused XML/HTML4 models show zero descriptor growth and preserve URL/encoding/options behavior; release leaks one descriptor per parse.This is issue-first because Nokogiri requires behavioral tests/changelog changes and the consumer audit does not modify upstream tests.