{"record":{"id":"e311a0eca36fd1d6","repo":"microsoft/qlib","slug":"please-implement-the-end-method","errorCode":null,"errorMessage":"Please implement the `end` method.","messagePattern":"Please implement the `end` method\\.","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"qlib/workflow/exp.py","lineNumber":72,"sourceCode":"        resume : bool\r\n            whether to resume the first recorder\r\n\r\n        Returns\r\n        -------\r\n        An active recorder.\r\n        \"\"\"\r\n        raise NotImplementedError(f\"Please implement the `start` method.\")\r\n\r\n    def end(self, recorder_status=Recorder.STATUS_S):\r\n        \"\"\"\r\n        End the experiment.\r\n\r\n        Parameters\r\n        ----------\r\n        recorder_status : str\r\n            the status the recorder to be set with when ending (SCHEDULED, RUNNING, FINISHED, FAILED).\r\n        \"\"\"\r\n        raise NotImplementedError(f\"Please implement the `end` method.\")\r\n\r\n    def create_recorder(self, recorder_name=None):\r\n        \"\"\"\r\n        Create a recorder for each experiment.\r\n\r\n        Parameters\r\n        ----------\r\n        recorder_name : str\r\n            the name of the recorder to be created.\r\n\r\n        Returns\r\n        -------\r\n        A recorder object.\r\n        \"\"\"\r\n        raise NotImplementedError(f\"Please implement the `create_recorder` method.\")\r\n\r\n    def search_records(self, **kwargs):\r\n        \"\"\"\r","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/workflow/exp.py#L54-L90","documentation":"Experiment.end(recorder_status) is an abstract stub in qlib/workflow/exp.py that raises NotImplementedError. It is meant to be overridden by concrete backends such as MLflowExperiment, where ending an experiment sets its active recorder's status (SCHEDULED, RUNNING, FINISHED, FAILED). The error signals an abstract or incompletely implemented Experiment subclass was used.","triggerScenarios":"Calling exp.end() on a bare Experiment instance; R.end_exp() routing to a custom Experiment subclass that does not override end; a subclass that overrides end_exp (manager-level) but not end (experiment-level).","commonSituations":"Building a custom tracking backend and implementing only the manager side (ExpManager) while leaving the Experiment side abstract; renaming methods while refactoring; upgrading qlib where the abstract interface gained methods.","solutions":["Use the shipped MLflowExperiment via qlib.init() instead of a hand-rolled Experiment.","In your subclass, implement end(self, recorder_status=Recorder.STATUS_S) to deactivate the experiment and finalize its recorder.","Audit your subclass against every method on the base Experiment (start, end, create_recorder, search_records, delete_recorder, _get_recorder, list_recorders)."],"exampleFix":"# before\nclass MyExp(Experiment):\n    def start(self, **kw):\n        ...\nexp.end()  # NotImplementedError from base end\n\n# after\nclass MyExp(Experiment):\n    def start(self, **kw): ...\n    def end(self, recorder_status=Recorder.STATUS_S):\n        # finalize recorder with recorder_status\n        ...","handlingStrategy":"type-guard","validationCode":"from qlib.workflow.exp import Experiment\nassert exp.__class__.end is not Experiment.end, 'end() not implemented by this backend'","typeGuard":"def implements_end(exp) -> bool:\n    from qlib.workflow.exp import Experiment\n    return exp.__class__.end is not Experiment.end","tryCatchPattern":null,"preventionTips":["In custom backends, implement all of start/end/create_recorder together as a unit.","Cover the abstract interface with an abstractmethod-style checklist test in CI."],"tags":["qlib","abstract-method","not-implemented","experiment-tracking"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}