Dolibarr/dolibarr · error

ErrorDownloadDocumentHooks

Error message

ErrorDownloadDocumentHooks: ${errors}

What it means

When a module's 'downloadDocument' hook returns a negative reshook, document.php aggregates $hookmanager->error and $hookmanager->errors, prints 'ErrorDownloadDocumentHooks: <errors>' and aborts the download. It surfaces hook-side rejection of a file download from htdocs/document.php:366-372.

Solutions

  1. Read the printed error list to identify which module's hook failed
  2. Check that module's downloadDocument hook implementation and its log/messages
  3. Update or disable the third-party module causing the negative reshook
  4. In your own hook, set $this->errors = array($msg) (an array) and return -1 only on real failures

Example fix

// before (hook)
$this->error = 'Cannot decrypt';
return -1;
// after
$this->errors = array('Cannot decrypt file '.$parameters['filename']);
return -1;
Defensive patterns

Strategy: try-catch

Try / catch

$reshook = $hookmanager->executeHooks('downloadDocument', $parameters, $object, $action);
if ($reshook < 0) {
    setEventMessages('Download blocked by hook: '.(is_array($hookmanager->errors) ? implode(', ', $hookmanager->errors) : $hookmanager->error), null, 'errors');
}

Prevention

When it happens

Trigger: A module implementing hook downloadDocument (e.g. an encryption, antivirus, or access-control module) returns -1 and populates $hookmanager->error or $hookmanager->errors while a user downloads a document through document.php.

Common situations: Antivirus/encryption modules failing to decrypt or scan the file; custom permission modules denying access; a hook throwing errors after a module upgrade changed its API; hook code setting ->errors as a string instead of an array.

Related errors


AI-assisted analysis of Dolibarr/dolibarr@598aa4bdad (2026-09-14). Data as JSON: /api/errors/4a75598c1ab11250. Report an issue: GitHub.

Appendix: source

Thrown at htdocs/document.php:370

// This test if file exists should be useless. We keep it to find bug more easily
if (!file_exists($fullpath_original_file_osencoded)) {
	dol_syslog("ErrorFileDoesNotExists: ".$fullpath_original_file);
	print $langs->trans("ErrorFileDoesNotExists") . ' : ' . dol_escape_htmltag($original_file);
	exit;
}

// Hooks
$hookmanager->initHooks(array('document'));
$parameters = array('ecmfile' => $ecmfile, 'modulepart' => $modulepart, 'original_file' => $original_file,
	'entity' => $entity, 'fullpath_original_file' => $fullpath_original_file,
	'filename' => $filename, 'fullpath_original_file_osencoded' => $fullpath_original_file_osencoded);
$object = new stdClass();
$reshook = $hookmanager->executeHooks('downloadDocument', $parameters, $object, $action); // Note that $action and $object may have been
if ($reshook < 0) {
	$errors = $hookmanager->error.(is_array($hookmanager->errors) ? (!empty($hookmanager->error) ? ', ' : '').implode(', ', $hookmanager->errors) : '');
	dol_syslog("document.php - Errors when executing the hook 'downloadDocument' : ".$errors);
	print "ErrorDownloadDocumentHooks: ".$errors;
	exit;
}

// Set this for test
//$type = 'text/html'; $attachment = -1;


// If we show an invoice, we test if we must regenerate the PDF
if ($modulepart == 'facture') {
	$refname = basename(dirname($original_file)."/");
	if ($refname == 'thumbs' || $refname == 'temp') {
		// If we get the thumbs directory, we must go one step higher. For example original_file='10/thumbs/myfile_small.jpg' -> refname='10'
		$refname = basename(dirname(dirname($original_file))."/");
	}

	$invoice = fetchObjectByElement(0, $modulepart, $refname);

	if ($original_file == preg_replace('/facture\//', '', $invoice->last_main_doc)) {

View on GitHub (pinned to 598aa4bdad)