Dolibarr/dolibarr · warning

Bad link. File is from another module part.

Error message

Bad link. File is from another module part.

What it means

When a file is resolved via hashp, viewimage.php fetches the EcmFiles record and compares its stored modulepart ($moduleparttocheck) with the modulepart in the URL. On mismatch it returns 403 'Bad link. File is from another module part.' — preventing access to a file by pretending it belongs to a different module directory.

Solutions

  1. Regenerate the link with the correct modulepart matching the ecm file record
  2. Omit modulepart entirely for hashp links — the script will derive it from the record
  3. Fix the ecm_files record if it was created with the wrong modulepart (update llx_ecm_files.src_object/modulepart)
  4. Check the medias/fckeditor backward-compat mapping if upgrading from old links

Example fix

// before
$url = DOL_URL_ROOT.'/viewimage.php?modulepart=fckeditor&hashp='.$hashp;
// after
$url = DOL_URL_ROOT.'/viewimage.php?hashp='.$hashp; // modulepart derived from record
Defensive patterns

Strategy: validation

Validate before calling

$ecmfile = new EcmFiles($db); $ecmfile->fetch(0, '', $relpath);
if (!empty($modulepart) && $ecmfile->src_object_type_ref !== $modulepart) { throw new RuntimeException('modulepart mismatch'); }

Prevention

When it happens

Trigger: Request viewimage.php?modulepart=X&hashp=<hash> where the hash resolves to a file registered under modulepart Y; e.g. a copied link whose modulepart was edited, or the file was moved/re-registered under another modulepart after the link was created.

Common situations: Links generated before a module refactor changed modulepart names (e.g. fckeditor -> medias); documents migrated between modules leaving stale hashp links; users sharing links and manually changing modulepart.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

Thrown at htdocs/viewimage.php:234

	include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
	include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
	$ecmfile = new EcmFiles($db);
	$result = $ecmfile->fetch(0, '', '', '', $hashp);
	if ($result > 0) {
		$tmp = explode('/', $ecmfile->filepath, 2); // $ecmfile->filepath is relative to document directory
		// filepath can be 'users/X' or 'X/propale/PR11111'
		if (is_numeric($tmp[0])) { // If first tmp is numeric, it is subdir of company for multicompany, we take next part.
			$tmp = explode('/', $tmp[1], 2);
		}
		$moduleparttocheck = $tmp[0]; // moduleparttocheck is first part of path

		if ($modulepart) {	// Not required, so often not defined, for link using public hashp parameter.
			if ($moduleparttocheck == $modulepart) {
				// We remove first level of directory
				$original_file = (($tmp[1] ? $tmp[1].'/' : '').$ecmfile->filename); // this is relative to module dir
				//var_dump($original_file); exit;
			} else {
				httponly_accessforbidden('Bad link. File is from another module part.', 403);
			}
		} else {
			$modulepart = $moduleparttocheck;
			$original_file = (($tmp[1] ? $tmp[1].'/' : '').$ecmfile->filename); // this is relative to module dir
		}

		if ($extname) {
			$original_file = getImageFileNameForSize($original_file, $extname);
		}
	} else {
		httponly_accessforbidden("ErrorFileNotFoundWithSharedLink", 403, 1);
	}
}

// Define mime type
$type = 'application/octet-stream';
if (GETPOST('type', 'alpha')) {
	$type = GETPOST('type', 'alpha');

View on GitHub (pinned to 598aa4bdad)