#2376·labelme

Config robustness: never crash, validate, degrade gracefully

Author: wkentaroCreated Jul 20, 2026Updated Sep 16, 2026
Labelswayfinder:mapwayfinder:config

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() — merges default_config.yaml <- user YAML <- overrides; runs migration then _update_dict.
  • _update_dict() / _validate_config_item() — raise ValueError for unknown keys, non-mapping sections, bad enums, duplicate labels.
  • _migrate_config_from_file() — the recurring crash source (e.g. re.match on a non-string ai.default).
  • Call sites: labelme/__main__.py:327 already wraps the early read in except Exception; labelme/_app.py:1223 _load_config() only catches ValueError, 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-construction Canvas.__init__, consumption-time QColor/label_colors), which is why bad config crashes past _load_config's except ValueError — a typed schema (#2380) only helps if it pulls layers 2-3 to load-time. Prior art: labelme/_config/_schema.py already types the 8 GUI-exposed keys. logger_level is 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_level config key (surfaced by the inventory) — a cosmetic cleanup, not a robustness change; file as a separate normal issue.