{"record":{"id":"1881353c2a009dbd","repo":"microsoft/qlib","slug":"please-implement-the-end-exp-method","errorCode":null,"errorMessage":"Please implement the `end_exp` method.","messagePattern":"Please implement the `end_exp` method\\.","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"qlib/workflow/expm.py","lineNumber":117,"sourceCode":"        \"\"\"\n        End an active experiment.\n\n        Maintaining `_active_exp_uri` is included in end_exp, remaining implementation should be included in _end_exp in subclass\n\n        Parameters\n        ----------\n        experiment_name : str\n            name of the active experiment.\n        recorder_status : str\n            the status of the active recorder of the experiment.\n        \"\"\"\n        self._active_exp_uri = None\n        # The subclass may set the underlying uri back.\n        # So setting `_active_exp_uri` come before `_end_exp`\n        self._end_exp(recorder_status=recorder_status, **kwargs)\n\n    def _end_exp(self, recorder_status: Text = Recorder.STATUS_S, **kwargs):\n        raise NotImplementedError(f\"Please implement the `end_exp` method.\")\n\n    def create_exp(self, experiment_name: Optional[Text] = None):\n        \"\"\"\n        Create an experiment.\n\n        Parameters\n        ----------\n        experiment_name : str\n            the experiment name, which must be unique.\n\n        Returns\n        -------\n        An experiment object.\n\n        Raise\n        -----\n        ExpAlreadyExistError\n        \"\"\"","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/workflow/expm.py#L99-L135","documentation":"ExpManager.end_exp clears _active_exp_uri then delegates to _end_exp, an abstract hook whose base body raises NotImplementedError. It fires when the active manager is the base ExpManager or an incomplete subclass, i.e. no backend logic exists to end the experiment and finalize its recorder status.","triggerScenarios":"R.end_exp() with a custom or bare exp_manager lacking _end_exp; calling end_exp after replacing the default MLflowExpManager in C['exp_manager'].","commonSituations":"Skeleton custom tracking managers that implement start but not end; test doubles subclassing ExpManager without full coverage of hooks.","solutions":["Use the default MLflowExpManager via qlib.init().","Implement _end_exp(self, recorder_status=Recorder.STATUS_S, **kwargs) in your subclass to close the active experiment.","Ensure the whole hook set (_start_exp, _end_exp, create_exp, _get_exp, delete_exp, list_experiments, search_records) is implemented."],"exampleFix":"# before\nclass MyManager(ExpManager):\n    pass\nR.end_exp()  # NotImplementedError from base _end_exp\n\n# after\nclass MyManager(ExpManager):\n    def _end_exp(self, recorder_status=Recorder.STATUS_S, **kwargs):\n        self.active_experiment.set_status(recorder_status)","handlingStrategy":"type-guard","validationCode":"from qlib.workflow.expm import ExpManager\nassert R.exp_manager.__class__._end_exp is not ExpManager._end_exp, '_end_exp not implemented'","typeGuard":"def manager_can_end(mgr) -> bool:\n    from qlib.workflow.expm import ExpManager\n    return mgr.__class__._end_exp is not ExpManager._end_exp","tryCatchPattern":null,"preventionTips":["Pair every _start_exp implementation with _end_exp in custom managers.","Prefer R.end_exp() with the shipped MLflow manager."],"tags":["qlib","abstract-method","not-implemented","exp-manager"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}