Config robustness: never crash, validate, degrade gracefully
Destination
labelme ships a hardened config subsystem: a malformed ~/.labelmerc or --config file never hard-crashes the app; invalid values produce informative per-field errors instead of tracebacks; config is validated against a typed schema; valid settings survive alongside invalid ones per the chosen degradation policy; and every migration is uniformly type-tolerant. Backward-compatible with existing YAML config files. Reaching the end of this map means this is designed and shipped, not merely specced.
Notes
Execution is carried in-map (plan + execute): a ticket whose decision is settled may resolve by shipping a PR (via /make-pr, CHANGELOG per repo convention, /review-fix), not only by recording a decision.
Domain surface (labelme/_config/__init__.py):
load_config()— mergesdefault_config.yaml<- user YAML <- overrides; runs migration then_update_dict._update_dict()/_validate_config_item()— raiseValueErrorfor unknown keys, non-mapping sections, bad enums, duplicate labels._migrate_config_from_file()— the recurring crash source (e.g.re.matchon a non-stringai.default).- Call sites:
labelme/__main__.py:327already wraps the early read inexcept Exception;labelme/_app.py:1223 _load_config()only catchesValueError, so any other exception hard-crashes before the window opens. Schema source of truth:labelme/_config/default_config.yaml.
Constraint: stay backward-compatible with existing user YAML config files.
Interim: PR #2358 (per-field guard for a non-string ai.default) may land now as an interim crash-fix; this overhaul supersedes ad-hoc per-field guards later. Track, don't block.
Skills: /grilling + /domain-modeling for HITL tickets; /prototype where "what shape" dominates; /make-pr + /review-fix for execution.
Tracker convention: GitHub, body-convention wayfinding. Membership = label wayfinder:config; type = wayfinder:<type>; blocking = Blocked by: line in each ticket body; claim = assign to wkentaro. See docs/agents/issue-tracker.md -> Wayfinding operations.
Decisions so far
- Inventory of every config key, type, and constraint — full per-key type/constraint/consumption table. Key finding: validation is scattered across 3 layers (load-time
_config, widget-constructionCanvas.__init__, consumption-timeQColor/label_colors), which is why bad config crashes past_load_config'sexcept ValueError— a typed schema (#2380) only helps if it pulls layers 2-3 to load-time. Prior art:labelme/_config/_schema.pyalready types the 8 GUI-exposed keys.logger_levelis dead config (spun off, see below).
Not yet specified
- Implementation tickets for the validator, the typed schema, and the migration rework (graduate once the pipeline/schema design lands).
- Error-surfacing UX rework: dialog shows all errors at once and keeps the partial config (graduates from the degradation-policy + validator-design decisions).
- Test strategy for the hardened subsystem: a corpus of malformed-config fixtures, cross-field-constraint cases.
Out of scope
- Changing the config file format (YAML stays); a new config/settings-dialog UI redesign.
- Remote / multi-file / layered config sources beyond the current default + user + overrides chain.
- Removing the dead
logger_levelconfig key (surfaced by the inventory) — a cosmetic cleanup, not a robustness change; file as a separate normal issue.
Source: wkentaro/labelme