{"record":{"id":"9caec05b5d9d7b54","repo":"passbolt/passbolt_api","slug":"the-directory-structure-cannot-be-retrieved","errorCode":null,"errorMessage":"The directory structure cannot be retrieved.","messagePattern":"The directory structure cannot be retrieved\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltEe/DirectorySync/src/Controller/DirectorySettingsController.php","lineNumber":146,"sourceCode":"\n        try {\n            $settings = LdapConfigurationForm::formatFormDataToOrgSettings($data);\n            $orgSettings = new DirectoryOrgSettings($settings);\n            $directory = DirectoryFactory::get($orgSettings);\n            $filteredDirectoryResults = $directory->getFilteredDirectoryResults();\n            $outputData = [\n                'users' => $this->_toArray(array_values($filteredDirectoryResults->getUsers())),\n                'groups' => $this->_toArray(array_values($filteredDirectoryResults->getGroups())),\n            ];\n        } catch (Exception $e) {\n            throw new BadRequestException('The users and groups cannot be retrieved. ' . $e->getMessage());\n        }\n\n        try {\n            $outputData['tree'] = $this->_toArray($filteredDirectoryResults->getTree());\n        } catch (Exception $e) {\n            $msg = __('The directory structure cannot be retrieved.');\n            throw new BadRequestException($msg . ' ' . $e->getMessage());\n        }\n\n        try {\n            $invalidObjects = $filteredDirectoryResults->getInvalidGroups();\n            $invalidObjects = array_merge($invalidObjects, $filteredDirectoryResults->getInvalidUsers());\n            $outputData['errors'] = $this->_toArray($invalidObjects);\n        } catch (Exception $e) {\n            $msg = __('There was an issue while retrieving the invalid entries.');\n            throw new BadRequestException($msg . ' ' . $e->getMessage());\n        }\n\n        $this->success(__('The operation was successful.'), $outputData);\n    }\n\n    /**\n     * Disable the ldap integration.\n     *\n     * @return void","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/DirectorySync/src/Controller/DirectorySettingsController.php#L128-L164","documentation":"Thrown by the DirectorySettingsController::test() endpoint when converting the LDAP directory tree (getTree()) to an array for the API response fails. This endpoint runs a dry-run of the LDAP configuration against the live directory and returns users, groups and their hierarchy; a failure here means the tree data returned by the LDAP connection was inconsistent or an exception occurred while serializing it.","triggerScenarios":"POST /directorysync/settings/test with valid LDAP settings where the resulting FilteredDirectoryResults object's getTree() throws during _toArray() — e.g. malformed group memberships, entries referencing missing parents, or LDAP attributes in unexpected formats.","commonSituations":"LDAP servers with cyclic or broken group parent relationships; partially migrated AD/LDAP trees; entries deleted between the fetch of users/groups and the tree build; custom LDAP schemas producing unexpected attribute types.","solutions":["Read the appended $e->getMessage() in the response body to identify the underlying serialization/tree error","Check the LDAP directory for broken group memberships or orphaned entries (groups whose parent DN does not exist)","Re-run the test endpoint after fixing LDAP data; compare with a fresh ldapsearch output","If triggered by specific users/groups, exclude them via filters in the LDAP settings and re-test"],"exampleFix":"// before: broken tree from cyclic group membership\ngroupOfNames -> memberOf -> groupOfNames (cycle)\n// after: remove the cycle or filter the offending group in the LDAP filter settings\n'groupFilter' => '(&(objectClass=groupOfNames)(!(memberOf=<bad-parent-dn>)))'","handlingStrategy":"try-catch","validationCode":"// client-side pre-check before calling the test endpoint\nif (!settings.ldap.host || !settings.ldap.port) throw new Error('LDAP host/port required');","typeGuard":"$tree = $filteredDirectoryResults->getTree();\nif (!is_array($tree)) {\n    throw new BadRequestException('Directory tree is not a traversable structure.');\n}","tryCatchPattern":"try {\n    $res = await api.post('/directorysync/settings/test', settings);\n} catch (e) {\n    if (e.message.includes('directory structure cannot be retrieved')) {\n        logLdapTreeIssue(e.message); // underlying cause appended after the message\n    }\n}","preventionTips":["Keep LDAP group parent relationships consistent (no cycles, no orphaned groups)","Test LDAP config with the /settings/test endpoint before saving","Filter out malformed entries via LDAP filter settings","Monitor logs/error.log for recurring tree serialization warnings"],"tags":["ldap","directory-sync","bad-request","serialization"],"backgroundTag":"unexpected-response-shape","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}