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
- Regenerate the link with the correct modulepart matching the ecm file record
- Omit modulepart entirely for hashp links — the script will derive it from the record
- Fix the ecm_files record if it was created with the wrong modulepart (update llx_ecm_files.src_object/modulepart)
- 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
- For hashp links, omit modulepart and let the server derive it
- Regenerate links after module migrations/renames (e.g. fckeditor -> medias)
- Keep llx_ecm_files modulepart values consistent with the files' locations
- Never hand-edit modulepart in shared URLs
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
- ErrorFileNotFoundWithSharedLink
- Sorry, your application is offline.
- You are logged with user
- Sorry, your application is offline. Only administrator users
- Access refused by IP protection. Your detected IP is
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)