{"record":{"id":"c6ffef9bfc6e46b8","repo":"Dolibarr/dolibarr","slug":"bad-link-bad-value-for-parameter-modulepart","errorCode":null,"errorMessage":"Bad link. Bad value for parameter modulepart","messagePattern":"Bad link\\. Bad value for parameter modulepart","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"htdocs/document.php","lineNumber":153,"sourceCode":" * @var DoliDB $db\n * @var HookManager $hookmanager\n * @var Translate $langs\n * @var User $user\n */\nrequire_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';\nrequire_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';\n\n$encoding = '';\n$action = GETPOST('action', 'aZ09');\n$original_file = GETPOST('file', 'alphanohtml');\n$hashp = GETPOST('hashp', 'aZ09');\n$modulepart = GETPOST('modulepart', 'alpha');\n$urlsource = GETPOST('urlsource', 'alpha');\n$entity = ($entity > 0 ? $entity : $conf->entity);\n\n// Security check\nif (empty($modulepart) && empty($hashp)) {\n\thttponly_accessforbidden('Bad link. Bad value for parameter modulepart', 400);\n}\nif (empty($original_file) && empty($hashp)) {\n\thttponly_accessforbidden('Bad link. Missing identification to find file (original_file or hashp)', 400);\n}\nif ($hashp == 'shared') {\n\thttponly_accessforbidden('Bad link. Bad value for parameter hashp', 400);\n}\nif ($modulepart == 'fckeditor') {\n\t$modulepart = 'medias'; // For backward compatibility\n}\n\n$socid = 0;\nif ($user->socid > 0) {\n\t$socid = $user->socid;\n}\n\n// For some module part, dir may be privates\nif (in_array($modulepart, array('facture_paiement', 'unpaid'))) {","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/Dolibarr/dolibarr/blob/598aa4bdada683d17ca04b1842548821ff0eb6c6/htdocs/document.php#L135-L171","documentation":"htdocs/document.php serves downloaded documents. On entry it validates GET parameters: if neither modulepart nor the public hashp is provided it calls httponly_accessforbidden() with this message and HTTP 400, because modulepart identifies which module's document directory the file belongs to.","triggerScenarios":"Calling document.php with neither modulepart nor hashp in the query string, e.g. document.php?original_file=...&entity=1 — the security check `empty($modulepart) && empty($hashp)` fires.","commonSituations":"Hand-built or broken download links; templates generating links that dropped the modulepart parameter; third-party code constructing document URLs without the required parameter; links truncated by encoders.","solutions":["Add modulepart to the URL, e.g. document.php?modulepart=invoice&original_file=...","Or use a shared link with hashp if the file is shared publicly","Regenerate the link via Dolibarr's API/templating (dol_buildpath / GETDOCURL helpers) instead of hand-writing it","Check calling code for empty/stripped query parameters"],"exampleFix":"// before\necho DOL_URL_ROOT.'/document.php?original_file='.$file;\n// after\necho DOL_URL_ROOT.'/document.php?modulepart=facture&original_file='.$file;","handlingStrategy":"validation","validationCode":"if (empty($_GET['modulepart']) && empty($_GET['hashp'])) {\n  http_response_code(400); exit('modulepart or hashp required');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always include modulepart in generated document URLs","Validate link templates render all query parameters","Escape/encode URLs fully so parameters are not lost","Add automated tests that all generated download links contain modulepart"],"tags":["http-400","download","validation"],"backgroundTag":"missing-required-argument","analyzedSha":"598aa4bdada683d17ca04b1842548821ff0eb6c6","analyzedAt":"2026-09-14T11:12:15.309Z","contentChangedAt":"2026-09-14T11:12:15.309Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}