{"record":{"id":"e6ec77b073d10b1d","repo":"passbolt/passbolt_api","slug":"invalid-phinxlog-migration-entity-end-time-property-not","errorCode":null,"errorMessage":"Invalid phinxlog migration entity, end_time property not defined.","messagePattern":"Invalid phinxlog migration entity, end_time property not defined\\.","errorType":"exception","errorClass":null,"httpStatus":500,"severity":"error","filePath":"plugins/PassboltEe/DirectorySync/src/Service/DirectorySettings/FixDirectorySyncLegacyFieldsMappingService.php","lineNumber":85,"sourceCode":"     * Check if the directory sync settings were created with a v3.\n     *\n     * @param \\App\\Model\\Entity\\OrganizationSetting $directorySyncSetting The directory sync settings\n     * @return bool\n     * @throws \\Exception If the migration V400ChangeLdapServersConfigKey cannot be found\n     * @throws \\Exception If the migration V400ChangeLdapServersConfigKey format is invalid\n     */\n    private function isDirectorySyncSettingsCreatedWithV3(OrganizationSetting $directorySyncSetting): bool\n    {\n        /** @var \\Cake\\ORM\\Entity|null $migration */\n        $migration = $this->phinxlogTable->find()\n            ->where(['migration_name' => 'V400ChangeLdapServersConfigKey'])\n            ->first();\n\n        if (is_null($migration)) {\n            throw new Exception('Unable to retrieve the migration V400ChangeLdapServersConfigKey.');\n        }\n        if (property_exists($migration, 'end_time')) {\n            throw new Exception('Invalid phinxlog migration entity, end_time property not defined.');\n        }\n\n        return $directorySyncSetting->created->lessThan($migration->get('end_time'));\n    }\n\n    /**\n     * Get and assert the directory sync settings.\n     *\n     * @param \\App\\Model\\Entity\\OrganizationSetting $directorySyncSettings The directory sync settings\n     * @return array\n     * @throws \\UnexpectedValueException If the directory sync settings are invalid\n     */\n    private function getAndAssertDirectorySyncDefaultV3FieldsMapping(OrganizationSetting $directorySyncSettings): array\n    {\n        $value = json_decode($directorySyncSettings->value, true);\n\n        if (\n            !$value","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/DirectorySync/src/Service/DirectorySettings/FixDirectorySyncLegacyFieldsMappingService.php#L67-L103","documentation":"After locating the V400ChangeLdapServersConfigKey phinxlog row, isDirectorySyncSettingsCreatedWithV3 asserts the entity exposes the migration end_time, which it needs to compare against the settings' created date. If the expected end_time property is absent from the hydrated entity it throws this Exception. Note the guard as written is inverted (property_exists(...) true → throw), so it also fires when the property IS defined, making the condition itself a latent bug worth flagging.","triggerScenarios":"Running the legacy fields-mapping fix when the Phinxlog entity hydration doesn't match expectations, or — because of the inverted property_exists condition — whenever the hydrated entity actually contains an end_time property (the normal case with a functioning phinxlog schema).","commonSituations":"A phinxlog table with an unexpected schema (custom columns or mapping producing an entity where end_time exists, triggering the inverted check); passbolt core changing the Phinxlog entity class; running the fix against a DB whose phinxlog entity was customized.","solutions":["Check the condition: as written it throws when property_exists($migration, 'end_time') is TRUE; it should be if (!property_exists(...)) — patch the plugin locally or update passbolt to a version with the corrected guard.","Inspect the hydrated entity (var_dump(get_object_vars($migration))) to see which properties are actually present.","Ensure the passbolt version running the fix matches the plugin code (this service shipped in 4.2.0); upgrade the DirectorySync plugin alongside core.","Report/verify against upstream passbolt issue tracker if the inverted condition reproduces on a stock install."],"exampleFix":"// before\nif (property_exists($migration, 'end_time')) {\n    throw new Exception('Invalid phinxlog migration entity, end_time property not defined.');\n}\n// after\nif (!property_exists($migration, 'end_time') && !$migration->has('end_time')) {\n    throw new Exception('Invalid phinxlog migration entity, end_time property not defined.');\n}","handlingStrategy":"try-catch","validationCode":"$migration = $phinxlog->find()->where(['migration_name' => 'V400ChangeLdapServersConfigKey'])->first();\n$hasEndTime = $migration && (property_exists($migration, 'end_time') || $migration->has('end_time'));","typeGuard":null,"tryCatchPattern":"try {\n    (new FixDirectorySyncLegacyFieldsMappingService())->fix();\n} catch (Exception $e) {\n    // inspect phinxlog entity properties / check passbolt version alignment\n}","preventionTips":["Keep the DirectorySync plugin and passbolt core versions aligned","Inspect the hydrated phinxlog entity properties if this fires unexpectedly","Be aware the guard condition appears inverted; verify against your passbolt version","Pin plugin versions during upgrades and test the fix on a staging DB"],"tags":["passbolt","directory-sync","phinxlog","inverted-condition","upgrade"],"backgroundTag":"internal-invariant-violation","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"}