{"record":{"id":"9f05a648787d633c","repo":"docusealco/docuseal","slug":"failed-to-save-document","errorCode":null,"errorMessage":"Failed to save document","messagePattern":"Failed to save document","errorType":"exception","errorClass":"Pdfium::PdfiumError","httpStatus":null,"severity":"error","filePath":"lib/pdfium.rb","lineNumber":800,"sourceCode":"\n      run_presave_hooks\n\n      file_write_mem = FFI::MemoryPointer.new(FPDF_FILEWRITE.size)\n\n      file_write_struct = FPDF_FILEWRITE.new(file_write_mem)\n      file_write_struct[:version] = 1\n      file_write_struct[:WriteBlock] = FFI::Function.new(:int, %i[pointer pointer ulong]) do |_, data, size|\n        io.write(data.read_bytes(size))\n\n        1\n      end\n\n      result = Pdfium.FPDF_SaveAsCopy(@document_ptr, file_write_mem, flags)\n\n      if result.zero?\n        Pdfium.check_last_error('Failed to save document')\n\n        raise PdfiumError, 'Failed to save document'\n      end\n\n      io\n    end\n\n    def cleanup\n      ensure_not_closed!\n\n      Pdfium.FPDF_RemoveOrphanObjects(@document_ptr)\n    end\n\n    def standard_font\n      @standard_font ||= Pdfium.FPDFText_LoadStandardFont(@document_ptr, 'Helvetica')\n    end\n\n    def add_presave_hook(key, &block)\n      @presave_hooks[key] ||= block\n    end","sourceCodeStart":782,"sourceCodeEnd":818,"githubUrl":"https://github.com/docusealco/docuseal/blob/004a22c1c88109c7ba0b567df011a8cb13894001/lib/pdfium.rb#L782-L818","documentation":"Raised by Document#save when FPDF_SaveAsCopy returns 0. save first runs presave hooks (such as the cleanup hook Page#redact installs) and then check_last_error, which raises the richer message with the PDFium error code when one is set; this plain variant means a zero return with no recorded code. That usually indicates an internally inconsistent document after failed edits or an unsupportable flag combination. Note the write callback streams into the target io as it goes, so the io can hold partial output when this raises.","triggerScenarios":"Saving after page edits (redact, object removal) that left the page tree inconsistent; using incremental-save flags on a document loaded from memory; a presave hook mutating the document into a bad state; saving an already damaged PDF that pdfium repaired at load time.","commonSituations":"Redaction pipelines over malformed PDFs; documents processed by mixed toolchains; docs loaded from bytes then saved with non-default flags.","solutions":["Retry with default flags (Pdfium::FPDF_NO_INCREMENTAL) - incremental saves fail on memory-loaded documents","Save to a fresh StringIO or tempfile to rule out a bad io target","Call doc.cleanup before save to drop orphaned objects","If it persists, re-open the original and apply fewer edits per save; update libpdfium since SaveAsCopy fixes land regularly"],"exampleFix":"# before\nFile.open(out, 'wb') { |f| doc.save(f, flags: Pdfium::FPDF_ANNOT) }\n\n# after\nFile.open(out, 'wb') { |f| doc.save(f, flags: Pdfium::FPDF_NO_INCREMENTAL) }","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  File.open(out, 'wb') { |f| doc.save(f) }\nrescue Pdfium::PdfiumError => e\n  raise unless e.message == 'Failed to save document'\n  buffer = StringIO.new\n  doc.save(buffer, flags: Pdfium::FPDF_NO_INCREMENTAL) # retry, non-incremental\n  File.binwrite(out, buffer.string)\nend","preventionTips":["Default to FPDF_NO_INCREMENTAL; incremental save only works for file-backed loads","Treat the target io as dirty after a failed save - the callback may have written partial bytes","Run doc.cleanup before saving edited documents"],"tags":["pdfium","save","ffi","ruby"],"backgroundTag":"file-save-failed","analyzedSha":"004a22c1c88109c7ba0b567df011a8cb13894001","analyzedAt":"2026-08-21T13:38:23.343Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}