microsoft/qlib · error · ValueError
Please make sure the recorder has been created and started…
Error message
Please make sure the recorder has been created and started properly before getting artifact uri.
What it means
Raised by Recorder.local_path when self.artifact_uri is None. The artifact URI is only populated after the recorder (an MLflow run) has been created and started; calling local_path on a recorder that was never started leaves artifact_uri None, so there is nothing to resolve into a local directory path.
Solutions
- Ensure the recorder is started before touching artifacts: use exp.get_recorder() only after R.start() and a successful run, or call recorder.start_run() first.
- Check recorder.artifact_uri is not None before calling local_path; if None, re-create the recorder via R.get_exp().create_recorder(...) + start_run().
- If the run was created through qlib.workflow.R, verify the MLflow tracking URI is reachable so start_run() actually persisted the run.
- If you truly have a valid run id, reload the recorder from the tracking store (e.g. MLflow client.get_run(id).info.artifact_uri) instead of using the un-started object.
Example fix
// before
recorder = exp.get_recorder()
path = recorder.local_path # ValueError if run never started
// after
recorder = exp.get_recorder()
if recorder.artifact_uri is None:
recorder.start_run()
path = recorder.local_path Defensive patterns
Strategy: validation
Validate before calling
if recorder.artifact_uri is None:
raise RuntimeError(f"Recorder {recorder.id} not started; artifact_uri is None")
path = recorder.local_path Try / catch
try:
path = recorder.local_path
except ValueError as e:
if "artifact uri" in str(e):
recorder.start_run()
path = recorder.local_path
else:
raise Prevention
- Always obtain recorders via R.get_recorder() after a completed run, not from a fresh experiment object.
- Assert recorder.artifact_uri is not None before any artifact/local-path access.
- Confirm the MLflow tracking server is reachable before starting workflows.
When it happens
Trigger: Calling recorder.local_path (directly or via APIs that save/read artifacts locally) on a Recorder instance obtained before start_run()/create_recorder was committed, e.g. R.get_recorder() on a fresh experiment where the run failed to start, or a Recorder constructed manually without starting.
Common situations: Experiment was created but the recorder start threw earlier (e.g. MLflow tracking server unreachable), user reuses a stale Recorder object after the run crashed, or the tracking backend is remote (the sibling RuntimeError branch also fires when the artifact dir is not local).
Related errors
- Error: . Something went wrong when deleting recorder…
- No valid recorder has been found, please make sure the…
- No valid recorder has been found, please make sure the…
- Error: . Something went wrong when deleting experiment…
- No valid experiment has been found, please make sure the…
AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15).
Data as JSON: /api/errors/d0e16d4a7dfa8365.
Report an issue: GitHub.
Appendix: source
Thrown at qlib/workflow/recorder.py:331
return self._artifact_uri
def get_local_dir(self):
"""
This function will return the directory path of this recorder.
"""
if self.artifact_uri is not None:
if platform.system() == "Windows":
local_dir_path = Path(self.artifact_uri.lstrip("file:").lstrip("/")).parent
else:
local_dir_path = Path(self.artifact_uri.lstrip("file:")).parent
local_dir_path = str(local_dir_path.resolve())
if os.path.isdir(local_dir_path):
return local_dir_path
else:
raise RuntimeError("This recorder is not saved in the local file system.")
else:
raise ValueError(
"Please make sure the recorder has been created and started properly before getting artifact uri."
)
def start_run(self):
# set the tracking uri
mlflow.set_tracking_uri(self.uri)
# start the run
run = mlflow.start_run(self.id, self.experiment_id, self.name)
# save the run id and artifact_uri
self.id = run.info.run_id
self._artifact_uri = run.info.artifact_uri
self.start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.status = Recorder.STATUS_R
logger.info(f"Recorder {self.id} starts running under Experiment {self.experiment_id} ...")
# NOTE: making logging async.
# - This may cause delay when uploading results
# - The logging time may not be accurate
View on GitHub (pinned to 79633dd950)