Skip to content

Stop on a malformed config or bindings file - #63

Merged
844196 merged 2 commits into
mainfrom
fix/59-malformed-config
Sep 2, 2026
Merged

Stop on a malformed config or bindings file#63
844196 merged 2 commits into
mainfrom
fix/59-malformed-config

Conversation

@844196

@844196 844196 commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Closes #59

Summary

A broken config file was indistinguishable from an absent one, so a typo silently changed how wk behaves. Deno.errors.NotFound is now the only silent fallback; anything else — a syntax error, a shape mismatch, EACCES, EISDIR — raises a ConfigError and exits 7.

Background

Condition Before
syntax error in config.yaml falls back to the defaults, exit 0
syntax error in bindings.yaml treated as empty, every key undefined, exit 5
bindings.yaml holds a mapping or a scalar no error — [].concat({foo:'bar'}) yields a one-element list and the bogus entry reaches the menu
config.yaml holds a scalar (42) no error — mergeContext(42) passes

Two causes. .catch(() => fallback) in run.ts could not tell "the file is not there" from "the file is broken", and nothing validated the shape of what was parsed.

Changes

  • src/errors.ts: add ConfigError, carrying the path and the reason.
  • src/run.ts:
    • rewrite loadYaml: only NotFound falls back, everything else raises ConfigError.
    • an empty document (null, comments only, ---, ~) still falls back, keeping the behaviour (config, bindings): an empty config file crashes instead of falling back #58 landed.
    • the three files are read in a fixed order, one at a timeconfig.yaml, the global bindings.yaml, then the local wk.bindings.yaml — so the first broken file is the one reported and the rest are left unread. The previous Promise.all concurrency is dropped; it bought nothing, because tui.init()'s cursor-position query is a synchronous blocking read that stalled those loads anyway.
    • one line on stderr, with no wk: prefix since the widget adds its own, and $HOME abbreviated to ~:
      • <path>: <first line of the @std/yaml message> for a syntax error — it already carries at line N, column M; the excerpt and caret that follow are dropped so the message fits zle -M.
      • <path>: invalid format for a shape mismatch.
  • src/widget.eta: comment only. Its *) arm already forwards an unknown exit code to zle -M, so no behavioural change.

Scope of the validation

A small hand-written check, so no new dependency:

  • config.yaml must be a non-null, non-array object.
  • both binding files must be arrays whose elements are non-null objects with a string key.

Field types (type, buffer, …) and nested bindings: are deliberately not checked.

Breaking change

Yes. A malformed config.yaml used to exit 0 and let wk keep running on the defaults; it now exits 7 and refuses to start. Input that used to succeed now fails, so this belongs in v2.0.0.

This PR alone does not ship v2.0.0. The field-type validation left out above is to be added first, and v2.0.0 released after that.

Verification

  • mise run check passes.
  • The full e2e suite passes (70 tests). e2e/tests/04_config.bats is now 19 tests: the two that pinned the silent fallbacks are updated to exit 7, and eight cases are added —
    • the local wk.bindings.yaml fails fast just like the global layer,
    • three shape mismatches (a mapping, a missing key, a scalar),
    • config is read before bindings, so the first broken file wins,
    • a directory in place of config.yaml,
    • a path under $HOME is reported with a tilde.

The new assertions match on the path prefix and at line N, column M rather than pinning @std/yaml's wording or the errno string verbatim, since e2e/helpers/common.bash states the suite may not reference Deno and doubles as an acceptance spec for any future reimplementation.

844196 and others added 2 commits September 3, 2026 00:56
A broken file was indistinguishable from an absent one: a syntax error in
config.yaml fell back to the defaults, a syntax error in bindings.yaml left
every key undefined, and a mapping or a scalar reached the menu untouched
because nothing checked the shape of what was parsed.

Deno.errors.NotFound is now the only silent fallback. Anything else raises a
ConfigError and exits 7, reporting `<path>: <reason>` on stderr with $HOME
abbreviated to `~`. The three files are read in a fixed order, so the first
broken one wins and the rest are left untouched.

BREAKING CHANGE: a malformed config.yaml used to exit 0 and let wk run on the
defaults; it now exits 7 and refuses to start.

Closes #59

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@844196 844196 self-assigned this Sep 2, 2026
@844196
844196 merged commit 916b1c4 into main Sep 2, 2026
3 checks passed
@844196
844196 deleted the fix/59-malformed-config branch September 2, 2026 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(config, bindings): a malformed config file is silently ignored

1 participant