Error handling is crucial when generating documents from templates and data sources. GroupDocs.Assembly for Python via .NET raises Python exceptions that help you identify and resolve issues with templates, data sources, or assembly operations. Understanding common error scenarios and how to handle them improves the reliability of your document generation code.
The exceptions you meet are:
RuntimeError - template syntax errors, missing members and other assembly errors
FileNotFoundError - a template or data file that does not exist, or an output folder that does not exist
PermissionError - a report path the process may not write
TypeError - a value that cannot be part of a data source, raised before the engine is called
Here are the steps to handle errors:
Wrap assembly operations in try/except blocks
Catch specific exception types
Validate data sources before assembly
Handle missing members gracefully
Log errors for debugging
Note
The message of an exception raised by the engine starts with the engine’s explanation, which names the tag that failed (“An error has been encountered at the end of expression ‘data.Email]>’”); the .NET exception type and stack trace follow on the next lines. FileNotFoundError and PermissionError raised by the engine are also RuntimeError, so catch them before RuntimeError.
Basic Error Handling
Wrap assembly operations in try/except blocks:
fromgroupdocs.assemblyimportDataSourceInfo,DocumentAssemblerdefengine_message(error):# The first line holds the engine's message; " ---> " starts the inner .NET exceptionreturnstr(error).splitlines()[0].split(" ---> ")[0]try:data={"Name":"Amara Osei","Age":41}assembler=DocumentAssembler()assembler.assemble_document("staff-greeting.docx","staff-greeting-report.docx",DataSourceInfo(data,"data"))print("Document assembled successfully.")exceptExceptionaserror:print("Error assembling document:",engine_message(error))# Handle error appropriately
A template with a malformed tag raises RuntimeError. The template of this example has a tag missing its closing bracket, <<[data.Name]>:
importosfromgroupdocs.assemblyimportDataSourceInfo,DocumentAssemblerdefengine_message(error):# The first line holds the engine's message; " ---> " starts the inner .NET exceptionreturnstr(error).splitlines()[0].split(" ---> ")[0]output_path="broken-tag-report.docx"try:data={"Name":"Amara Osei","Team":"Operations"}assembler=DocumentAssembler()assembler.assemble_document("broken-tag.docx",output_path,DataSourceInfo(data,"data"))exceptRuntimeErroraserror:print("Template error:",engine_message(error))# The failed call leaves an empty report file behindifos.path.exists(output_path)andos.path.getsize(output_path)==0:os.remove(output_path)# Log error, notify user, or use fallback templateexceptExceptionaserror:print("Unexpected error:",engine_message(error))
The following sample file is used in this example: broken-tag.docx
Template error: Expression tag is not well-formed. Character '>' is unexpected.
Template error: Expression tag is not well-formed. Character '>' is unexpected.
A call that fails on the template or the data has already created the report file, and leaves it empty (0 bytes); delete it, as above, so that nothing downstream mistakes it for a report.
Handle Missing Data Members
When the template reads a member the data does not have, the call raises RuntimeError: for the template below and a dict without an "Email" key, the message starts with “An error has been encountered at the end of expression ‘data.Email]>’. Can not get the value of member ‘Email’”. To output nothing for a missing member instead, set DocumentAssemblyOptions.ALLOW_MISSING_MEMBERS:
fromgroupdocs.assemblyimportDataSourceInfo,DocumentAssembler,DocumentAssemblyOptionsdefengine_message(error):# The first line holds the engine's message; " ---> " starts the inner .NET exceptionreturnstr(error).splitlines()[0].split(" ---> ")[0]try:# Data doesn't have all members referenced in template:# the template also reads data.Email, which doesn't existdata={"Name":"Amara Osei"}assembler=DocumentAssembler()# Allow missing members (they output nothing)assembler.options|=DocumentAssemblyOptions.ALLOW_MISSING_MEMBERSassembler.assemble_document("missing-email.docx","missing-email-report.docx",DataSourceInfo(data,"data"))exceptExceptionaserror:print("Error:",engine_message(error))
Validate data sources before assembly. In Python this matters more than in .NET: an empty list carries no item type, so a template that reads its items (<<[p.ProductName]>>) fails with “Can not get the value of member ‘ProductName’ on type ‘System.Object’” when the list is empty:
fromgroupdocs.assemblyimportDataSourceInfo,DocumentAssemblerdefengine_message(error):# The first line holds the engine's message; " ---> " starts the inner .NET exceptionreturnstr(error).splitlines()[0].split(" ---> ")[0]defassemble_catalog(products,output_path):# products might be empty or Noneifnotproducts:print("Warning: No products to process.")returntry:assembler=DocumentAssembler()assembler.assemble_document("product-catalog.docx",output_path,DataSourceInfo(products,"products"))print("Document assembled successfully.")exceptExceptionaserror:print("Error:",engine_message(error))assemble_catalog([{"ProductName":"Harbor Blend whole bean coffee","ProductPrice":"$18.40"},{"ProductName":"Chestnut honey","ProductPrice":"$7.80"},{"ProductName":"Smoked gouda","ProductPrice":"$15.20"},],"product-catalog-report.docx")assemble_catalog([],"product-catalog-empty.docx")
A template path that does not exist raises FileNotFoundError, and so does a report path whose folder does not exist. Check the path with os.path.exists() before the call, catch the exception, or both:
fromgroupdocs.assemblyimportDataSourceInfo,DocumentAssemblerdefassemble_greeting(template_path,output_path):try:data={"Name":"Amara Osei","Age":41}assembler=DocumentAssembler()assembler.assemble_document(template_path,output_path,DataSourceInfo(data,"data"))print("Document assembled successfully:",output_path)exceptFileNotFoundError:print("Template file not found:",template_path)exceptExceptionaserror:print("Error:",str(error).splitlines()[0])assemble_greeting("staff-greeting.docx","staff-greeting-checked.docx")# This template is not among the example's filesassemble_greeting("staff-greeting-2016.docx","staff-greeting-2016-report.docx")
The exception’s message is the engine’s (“Could not find file ‘’.”). A missing template does not leave a report file behind.
Handle Missing Values
A None value is not an error: the tag outputs nothing, whatever its format, and the call succeeds. Here the invoice has no amount yet:
fromgroupdocs.assemblyimportDataSourceInfo,DocumentAssemblerdefengine_message(error):# The first line holds the engine's message; " ---> " starts the inner .NET exceptionreturnstr(error).splitlines()[0].split(" ---> ")[0]invoice={"CustomerName":"Quayside Bistro","Amount":None,# Not invoiced yet}try:assembler=DocumentAssembler()assembler.assemble_document("invoice-amount.docx","invoice-amount-report.docx",DataSourceInfo(invoice,"invoice"))exceptExceptionaserror:print("Data type error:",engine_message(error))# Handle None values or provide defaults
The template line Amount due: <<[invoice.Amount]:"C">> becomes Amount due:. When the report must show a value, replace None in Python before the call.
Comprehensive Error Handling
Combine multiple error handling strategies. Catch FileNotFoundError and PermissionError before RuntimeError, since the engine’s file errors are RuntimeError too:
importosimporttracebackfromgroupdocs.assemblyimportDataSourceInfo,DocumentAssembler,DocumentAssemblyOptionsdefengine_message(error):# The first line holds the engine's message; " ---> " starts the inner .NET exceptionreturnstr(error).splitlines()[0].split(" ---> ")[0]defassemble(template_path,output_path,data):# Validate inputsifnottemplate_pathornotos.path.exists(template_path):print("Invalid template path.")return# Validate dataifdataisNone:print("Data source is null.")returntry:assembler=DocumentAssembler()# Configure error handling optionsassembler.options|=DocumentAssemblyOptions.ALLOW_MISSING_MEMBERSassembler.assemble_document(template_path,output_path,DataSourceInfo(data,"data"))print("Document assembled successfully.")exceptFileNotFoundErroraserror:print("File not found:",engine_message(error))exceptPermissionErroraserror:print("Access denied:",engine_message(error))exceptRuntimeErroraserror:print("Assembly error:",engine_message(error))# Log detailed error information: the full message holds the .NET detailsexceptExceptionaserror:print("Unexpected error:",error)traceback.print_exc()assemble("missing-email.docx","missing-email-checked.docx",{"Name":"Jonas Weber","Age":37})
Instead of raising, the engine can write each template error into the report, at the tag that failed. Set DocumentAssemblyOptions.INLINE_ERROR_MESSAGES: assemble_document then returns False when the template has errors and True when it has none. The example saves the report as plain text to an io.BytesIO and prints the lines that hold an error:
importiofromgroupdocs.assemblyimport(DataSourceInfo,DocumentAssembler,DocumentAssemblyOptions,FileFormat,LoadSaveOptions)data={"Name":"Amara Osei","Team":"Operations"}assembler=DocumentAssembler()assembler.options=DocumentAssemblyOptions.INLINE_ERROR_MESSAGESwithopen("broken-tag.docx","rb")astemplate_stream,io.BytesIO()asoutput_stream:parsed=assembler.assemble_document(template_stream,output_stream,LoadSaveOptions(FileFormat.TEXT),DataSourceInfo(data,"data"))print("Template parsed without errors:",parsed)ifnotparsed:forlineinoutput_stream.getvalue().decode("utf-8-sig").splitlines():if"Error!"inline:print(line)
The following sample file is used in this example: broken-tag.docx
Template parsed without errors: False
Name: <<[data.Name]> Error! Character '>' is unexpected.
Template parsed without errors: False
Name: <<[data.Name]> Error! Character '>' is unexpected.
The report keeps the template text up to the error and marks it with “Error!”; after a syntax error, the tags that follow are left as they are (Team: <<[data.Team]>> above). A missing member is reported the same way: with INLINE_ERROR_MESSAGES and the missing-member template above, the call returns False and the report reads Email: <<[data.Email] Error! Can not get the value of member 'Email' ...>>. Combined with ALLOW_MISSING_MEMBERS (options = INLINE_ERROR_MESSAGES | ALLOW_MISSING_MEMBERS), a missing member is no error, and the call returns True.
Error Handling Best Practices
Always use try/except blocks around assembly operations
Validate inputs before assembly (file existence, None and empty lists)
Use specific exception types when possible, FileNotFoundError and PermissionError before RuntimeError
Enable ALLOW_MISSING_MEMBERS if your data structure is dynamic
Log errors with sufficient detail for debugging: the full message holds the .NET exception type and stack trace
Provide user-friendly error messages in production code, and delete the empty report a failed call leaves
Warning
Some errors only occur during template evaluation, with particular data. Always test templates with sample data before deploying to production; INLINE_ERROR_MESSAGES makes a test run list every error of a template at once.
Advanced Usage Topics
To learn more about detailed error logging, template validation, custom error handling, and debugging techniques, please refer to the advanced usage section.
More resources
GitHub Examples
You may easily run the code above and see the feature in action in our GitHub examples: