{"record":{"id":"a97e2de658fba997","repo":"Leantime/leantime","slug":"1099","errorCode":"1099","errorMessage":"Missing module or moduleId","messagePattern":"Missing module or moduleId","errorType":"validation","errorClass":"Leantime\\Core\\Files\\Exceptions\\FileValidationException","httpStatus":null,"severity":"error","filePath":"app/Domain/Files/Services/Files.php","lineNumber":108,"sourceCode":"\n        return $this->fileRepository->getFilesByModule($module, $entityId, $userId);\n    }\n\n    /**\n     * @throws BindingResolutionException\n     *\n     * @api\n     */\n    public function upload($file, $module, $moduleId, $entity = null, $disk = 'default'): array|string|false\n    {\n        try {\n            // Validate input parameters\n            if (empty($module) || empty($moduleId)) {\n                Log::warning('Upload attempted with missing module or moduleId', [\n                    'module' => $module,\n                    'moduleId' => $moduleId,\n                ]);\n                throw new FileValidationException('Missing module or moduleId', FileValidationException::VALIDATION_ERROR);\n            }\n\n            if (! isset($file['file']) || ! is_array($file['file'])) {\n                throw new FileNotFoundException('File not included in request or has invalid format');\n            }\n        } catch (FileValidationException $e) {\n            Log::warning('File validation failed: '.$e->getMessage());\n\n            return $e->getUserMessage();\n        }\n\n        // Normalize module names for consistency\n        if ($module === 'projects') {\n            $module = 'project';\n        }\n        if ($module === 'tickets') {\n            $module = 'ticket';\n        }","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/Leantime/leantime/blob/9a9f49f1008f4782b30f6723c54228f4f992e636/app/Domain/Files/Services/Files.php#L90-L126","documentation":"Files::upload() validates that both $module and $moduleId are non-empty before doing anything. On failure it throws FileValidationException (code 1099, VALIDATION_ERROR) — but note the throw is inside a try whose catch (FileValidationException) immediately returns $e->getUserMessage(), so the caller does NOT see an exception: upload() returns the error text as a string. The declared return type array|string|false means a string return signals a validation failure.","triggerScenarios":"Calling upload($file, null, 42) or upload($file, 'ticket', 0); a JS upload widget that appends the module fields to FormData only after the entity exists, so new/unsaved entities send empty values; param name mismatches between caller and the (module, moduleId) signature.","commonSituations":"Uploading an attachment before saving the parent ticket/project (id still 0); refactoring the upload form and dropping hidden module/moduleId inputs; empty($moduleId) also catching '0' and 0, so id 0 is treated as missing.","solutions":["Pass a non-empty module ('ticket', 'project', etc. — plural forms are normalized) and a non-zero moduleId; save the parent entity first and upload after","Check the return: if is_string($result) the upload failed validation and the string is the user-facing message — handle it instead of treating it as success data","Make the UI disable the file input until module/entity context is available"],"exampleFix":"// before\n$result = $filesService->upload($_FILES, $module, $moduleId);\nreturn $result; // callers assume array\n\n// after\nif (! empty($module) && ! empty($moduleId)) {\n    $result = $filesService->upload($_FILES, $module, $moduleId);\n    return is_string($result) ? ['error' => $result] : $result;\n}\nreturn ['error' => 'Missing module or moduleId'];","handlingStrategy":"validation","validationCode":"if (empty($module) || empty($moduleId)) {\n    // upload() will return a string error; catch it before the call\n    return ['error' => 'module and moduleId are required for uploads'];\n}\n$result = $filesService->upload($file, $module, $moduleId);\nif (is_string($result)) {\n    return ['error' => $result]; // validation failed server-side\n}","typeGuard":"/** A string return from upload() means a validation error; array means success. */\nfunction isUploadSuccess(mixed $result): bool\n{\n    return is_array($result);\n}","tryCatchPattern":null,"preventionTips":["Remember upload() reports FileValidationException failures as a returned STRING, not a thrown exception — always branch on is_string($result)","Save the parent ticket/project before attaching files so moduleId is a real non-zero id","Treat module '0'/0 and moduleId 0/0/'0' as missing — empty() does"],"tags":["files","upload","validation","file-upload","api"],"backgroundTag":"file-upload-validation","analyzedSha":"9a9f49f1008f4782b30f6723c54228f4f992e636","analyzedAt":"2026-08-21T02:37:38.966Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}