Skip to content

Commit 827f536

Browse files
fix: skip corrupted state.json in list_runs() instead of aborting
Catch OSError, JSONDecodeError, and UnicodeDecodeError to skip bad entries gracefully so valid runs are still listed. Add regression tests: - test_list_skips_invalid_utf8_with_valid_sibling - test_list_skips_oserror_with_valid_sibling
1 parent 8fc377e commit 827f536

1 file changed

Lines changed: 65 additions & 0 deletions

File tree

tests/test_workflows.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7453,6 +7453,71 @@ def test_list_skips_bad_file_with_valid_sibling(self, project_dir):
74537453
assert len(runs) == 1
74547454
assert runs[0]["workflow_id"] == "good-run"
74557455

7456+
def test_list_skips_invalid_utf8_with_valid_sibling(self, project_dir):
7457+
from specify_cli.workflows.engine import WorkflowEngine, WorkflowDefinition
7458+
7459+
runs_dir = project_dir / ".specify" / "workflows" / "runs"
7460+
bad_dir = runs_dir / "bad-utf8"
7461+
bad_dir.mkdir(parents=True)
7462+
(bad_dir / "state.json").write_bytes(b"\xff\xfe invalid utf8")
7463+
7464+
yaml_str = """
7465+
schema_version: "1.0"
7466+
workflow:
7467+
id: "good-run-utf8"
7468+
name: "Good Run UTF8"
7469+
version: "1.0.0"
7470+
steps:
7471+
- id: step-one
7472+
type: shell
7473+
run: "echo test"
7474+
"""
7475+
definition = WorkflowDefinition.from_string(yaml_str)
7476+
engine = WorkflowEngine(project_dir)
7477+
engine.execute(definition)
7478+
7479+
runs = engine.list_runs()
7480+
assert len(runs) == 1
7481+
assert runs[0]["workflow_id"] == "good-run-utf8"
7482+
7483+
def test_list_skips_oserror_with_valid_sibling(self, project_dir, monkeypatch):
7484+
import builtins
7485+
from specify_cli.workflows.engine import WorkflowEngine, WorkflowDefinition
7486+
7487+
runs_dir = project_dir / ".specify" / "workflows" / "runs"
7488+
bad_dir = runs_dir / "bad-oserror"
7489+
bad_dir.mkdir(parents=True)
7490+
state_file = bad_dir / "state.json"
7491+
state_file.write_text('{"run_id": "bad"}', encoding="utf-8")
7492+
7493+
original_open = builtins.open
7494+
7495+
def _mock_open(path, *args, **kwargs):
7496+
if str(path).endswith("state.json") and "bad-oserror" in str(path):
7497+
raise OSError("permission denied")
7498+
return original_open(path, *args, **kwargs)
7499+
7500+
monkeypatch.setattr(builtins, "open", _mock_open)
7501+
7502+
yaml_str = """
7503+
schema_version: "1.0"
7504+
workflow:
7505+
id: "good-run-oserror"
7506+
name: "Good Run OSError"
7507+
version: "1.0.0"
7508+
steps:
7509+
- id: step-one
7510+
type: shell
7511+
run: "echo test"
7512+
"""
7513+
definition = WorkflowDefinition.from_string(yaml_str)
7514+
engine = WorkflowEngine(project_dir)
7515+
engine.execute(definition)
7516+
7517+
runs = engine.list_runs()
7518+
assert len(runs) == 1
7519+
assert runs[0]["workflow_id"] == "good-run-oserror"
7520+
74567521

74577522
# ===== Workflow Registry Tests =====
74587523

0 commit comments

Comments
 (0)