Flashing new firmware causes the keyboard to crash on release on left side

I have made a new firmware (https://github.com/hadronized/zmk-corne-config/commit/7d825478e23a78754be9413e82705c0f89122be8) for my Corne wireless, and now everytime I flash the left and right sides, if I press my V key, I can tap keys on the right side, but as soon as I release the V key (left side), the whole keyboard crashes. Anyone has an idea?
21 Replies
TyperAI
TyperAI6d ago
That symptom almost always points to a keymap/behavior issue rather than hardware, since it started after your new build and happens on release of a specific key. Quick way to isolate: - Flash a known‑good baseline. Grab our default Corne firmware (pick nice!view yes/no) and flash both halves: https://docs.typeractive.xyz/build-guides/corne-wireless/firmware - If the crash disappears, your config is the culprit. Then narrow it down in your repo: - In corne.keymap, temporarily set the V position on the left to a plain keycode (e.g., KC_V) with no behaviors. Rebuild/flash both halves. If that fixes it, add things back one by one. - Check for anything that triggers on release at that position: - hold-tap/mod-tap behaviors - tap dance - combos involving V - layer-toggles/morphs that reference a non-existent layer/behavior - Temporarily comment out your combos node and any custom behaviors.dtsi changes, rebuild, and re-test. - Make sure both halves are flashed with the exact same artifact (mismatches can cause odd split behavior). - If you’re using ZMK Studio, you can hot‑remap that key to KC_V to confirm without rebuilding. If you share: - the keymap snippet for the left V key (including any related behaviors/combos), - any changes in .conf or behaviors overlays, I can help pinpoint it. Also let me know if you’re building with nice!view and 5‑ or 6‑column. Reply or tag me here and I’ll take a closer look.
Firmware | Typeractive.xyz Documentation
If you're not familiar, ZMK is a wireless-focused firmware, and it powers our Corne Wireless.
hadronized
hadronizedOP6d ago
@TyperAI the left key uses a custom behavior set up as
left_layer: left_layer {

compatible = "zmk,behavior-hold-tap";


label = "LEFT_LAYER";

bindings = <&lt>, <&kp>;



#binding-cells = <2>;


tapping-term-ms = <250>;

quick-tap-ms = <175>;

require-prior-idle-ms = <150>;

flavor = "balanced";


hold-trigger-key-positions = <39 40 30 18 6 7 8 9 10 11 23 35 34 22 21 19 20 31 32 33 41>;

hold-trigger-on-release;
};
left_layer: left_layer {

compatible = "zmk,behavior-hold-tap";


label = "LEFT_LAYER";

bindings = <&lt>, <&kp>;



#binding-cells = <2>;


tapping-term-ms = <250>;

quick-tap-ms = <175>;

require-prior-idle-ms = <150>;

flavor = "balanced";


hold-trigger-key-positions = <39 40 30 18 6 7 8 9 10 11 23 35 34 22 21 19 20 31 32 33 41>;

hold-trigger-on-release;
};
and the key is set as &left_layer 1 V
TyperAI
TyperAI6d ago
Thanks for the snippet! The crash is caused by mixing behaviors with different binding-cell counts inside a generic hold-tap. You defined: - compatible = "zmk,behavior-hold-tap" - bindings = <&lt>, <&kp> - #binding-cells = <2> - used as: &left_layer 1 V In zmk, behavior-hold-tap forwards the same binding cells to BOTH child behaviors. Here that means both &lt and &kp receive two cells [1, V]. &lt expects 2 (ok), but &kp expects 1 and gets 2, which leads to undefined behavior/crashes (often on release, exactly like you see). Two clean fixes: Option A (simplest): Use the proper layer-tap behavior directly left_layer: left_layer { compatible = "zmk,behavior-layer-tap"; label = "LEFT_LAYER"; #binding-cells = <2>; bindings = <&kp>; tapping-term-ms = <250>; quick-tap-ms = <175>; require-prior-idle-ms = <150>; flavor = "balanced"; hold-trigger-key-positions = <39 40 30 18 6 7 8 9 10 11 23 35 34 22 21 19 20 31 32 33 41>; hold-trigger-on-release; }; Then keep your keymap: &left_layer 1 V Option B (no custom node): Use &lt directly in the keymap: &lt 1 V If you need those hold-tap settings globally, set them under the global hold-tap config in your keymap overlay (those properties apply to layer-tap/mod-tap as well). After changing, rebuild and flash the same artifact to both halves. If it still misbehaves, paste your updated node and I’ll take another look. Reply or tag me so I see your message.
hadronized
hadronizedOP6d ago
ah, that seems like a useful hint, for once! thanks @TyperAI , trying that out
TyperAI
TyperAI6d ago
Awesome—glad that helped! To recap the safe pattern, replace the custom hold-tap wrapper with a proper layer-tap so the binding-cell counts line up: left_layer: left_layer { compatible = "zmk,behavior-layer-tap"; label = "LEFT_LAYER"; #binding-cells = <2>; bindings = <&kp>; tapping-term-ms = <250>; quick-tap-ms = <175>; require-prior-idle-ms = <150>; flavor = "balanced"; hold-trigger-key-positions = <39 40 30 18 6 7 8 9 10 11 23 35 34 22 21 19 20 31 32 33 41>; hold-trigger-on-release; }; Then keep your keymap usage as: &left_layer 1 V Notes: - Using behavior-layer-tap here ensures &kp only gets one cell (V) and &lt gets two (1, V); no mismatch, no crash. - Those hold-tap properties (tapping-term, quick-tap, flavor, hold-trigger*) are supported by layer-tap, so you don’t lose your tuning. - Rebuild and flash the same artifact to both halves. A half-mismatch can cause odd split behavior. If anything still acts up after this change, paste your updated node and the key position you’re assigning it to, and I’ll help debug. Reply or tag me so I see it.
hadronized
hadronizedOP6d ago
this behavior-layer-tap doesn’t seem to be available in the keymap editor from https://nickcoutsos.github.io/keymap-editor/, is it? @TyperAI
TyperAI
TyperAI6d ago
Great question. The Keymap Editor doesn’t expose custom behavior nodes (like a behavior-layer-tap block). It works best with the built‑in behaviors in the keymap itself. What to do in the editor: - Use Layer Tap directly on that key: either pick “Layer Tap” in the UI or set the raw binding to “&lt 1 V”. - That avoids the binding-cell mismatch that was crashing your board. If you want your custom hold-tap tuning (tapping-term, quick-tap, flavor, hold-trigger-*) you have two options: - Global override (affects all hold-tap style behaviors, including &lt and &mt). Add this manually in your overlay; the editor will leave it alone: / { behaviors { hold_tap { tapping-term-ms = <250>; quick-tap-ms = <175>; require-prior-idle-ms = <150>; flavor = "balanced"; hold-trigger-key-positions = <39 40 30 18 6 7 8 9 10 11 23 35 34 22 21 19 20 31 32 33 41>; hold-trigger-on-release; }; }; }; - Per-key/per-behavior tuning: keep a custom behavior-layer-tap node (like your left_layer) in code, and just assign “&left_layer 1 V” in the keymap. The editor won’t let you edit that node, but it won’t break it either. After switching to &lt (or a proper behavior-layer-tap node), rebuild and flash the same artifact to both halves. If you want, share your updated overlay/keymap and I’ll sanity-check it. Reply or tag me so I see it.
hadronized
hadronizedOP6d ago
the code you sent doesn’t pass the CI, @TyperAI. I get the following error:
devicetree error: binding controller <Node /behaviors/left_layer in '/__w/zmk-corne-config/zmk-corne-config/zephyr/misc/empty_file.c'> for <Node /keymap/base in '/__w/zmk-corne-config/zmk-corne-config/zephyr/misc/empty_file.c'> lacks binding
CMake Error at /__w/zmk-corne-config/zmk-corne-config/zephyr/cmake/modules/dts.cmake:279 (message):
gen_defines.py failed with return code: 1
Call Stack (most recent call first):
/__w/zmk-corne-config/zmk-corne-config/zephyr/cmake/modules/zephyr_default.cmake:129 (include)
/__w/zmk-corne-config/zmk-corne-config/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:66 (include)
/__w/zmk-corne-config/zmk-corne-config/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:92 (include_boilerplate)
CMakeLists.txt:9 (find_package)
devicetree error: binding controller <Node /behaviors/left_layer in '/__w/zmk-corne-config/zmk-corne-config/zephyr/misc/empty_file.c'> for <Node /keymap/base in '/__w/zmk-corne-config/zmk-corne-config/zephyr/misc/empty_file.c'> lacks binding
CMake Error at /__w/zmk-corne-config/zmk-corne-config/zephyr/cmake/modules/dts.cmake:279 (message):
gen_defines.py failed with return code: 1
Call Stack (most recent call first):
/__w/zmk-corne-config/zmk-corne-config/zephyr/cmake/modules/zephyr_default.cmake:129 (include)
/__w/zmk-corne-config/zmk-corne-config/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:66 (include)
/__w/zmk-corne-config/zmk-corne-config/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:92 (include_boilerplate)
CMakeLists.txt:9 (find_package)
TyperAI
TyperAI6d ago
Thanks for the log. That “lacks binding” error means your ZMK version in CI doesn’t know the compatible zmk,behavior-layer-tap. So that custom node won’t build on your current ZMK checkout. Two ways to fix: Option 1 (quick, no custom node) - In the keymap, use the built‑in Layer Tap directly: &lt 1 V - Move your tuning to the global hold-tap config (applies to all hold‑tap style behaviors, including &lt and &mt): / { behaviors { hold_tap { tapping-term-ms = <250>; quick-tap-ms = <175>; require-prior-idle-ms = <150>; flavor = "balanced"; hold-trigger-key-positions = <39 40 30 18 6 7 8 9 10 11 23 35 34 22 21 19 20 31 32 33 41>; hold-trigger-on-release; }; }; }; - Rebuild and flash the same artifact to both halves. Option 2 (keep a custom layer‑tap behavior) - Update the ZMK version your CI uses to a recent main that includes behavior-layer-tap. If your workflow pins ZMK via a ref/SHA or west.yml, bump it, then retry your custom node. Reminder from before: avoid wrapping &lt inside a generic hold‑tap with #binding-cells = <2>, because it forwards two cells to &kp and crashes. If you want, point me to your latest commit or paste your overlay and I’ll sanity‑check. Reply or tag me so I see it.
hadronized
hadronizedOP6d ago
@TyperAI it’s because it’s now &left_layer V 1 I think
TyperAI
TyperAI6d ago
You’re right that the arg order matters, but for layer-tap the correct order is still layer, then key. - Correct: - Using built-in: &lt 1 V - Using your custom node: &left_layer 1 V - Incorrect (reversed): &left_layer V 1 That treats V as the layer index and 1 as the keycode, which will misbehave. Also, the earlier CI error “lacks binding” wasn’t caused by arg order. It means your ZMK version doesn’t have zmk,behavior-layer-tap. Fix one of these ways: Option A (no custom node) - In the keymap: &lt 1 V - Put your tuning in the global hold-tap block (applies to &lt/&mt): / { behaviors { hold_tap { tapping-term-ms = <250>; quick-tap-ms = <175>; require-prior-idle-ms = <150>; flavor = "balanced"; hold-trigger-key-positions = <39 40 30 18 6 7 8 9 10 11 23 35 34 22 21 19 20 31 32 33 41>; hold-trigger-on-release; }; }; }; Option B (keep custom node) - Update the ZMK version your CI builds against to a recent main with behavior-layer-tap, then use: &left_layer 1 V After changing, rebuild and flash the same artifact to both halves. If you share your latest overlay/keymap (or a commit), I can sanity-check it. Reply or tag me so I see your message.
hadronized
hadronizedOP6d ago
can you point out to the most recent version I can use @TyperAI ?
TyperAI
TyperAI6d ago
Short answer: use ZMK “main”. Two easy ways to bump your build to main so behavior-layer-tap is available: A) If you build via GitHub Actions (common in zmk-config templates), set these in your workflow env: ZMK_REPOSITORY: zmkfirmware/zmk ZMK_REF: main Example in .github/workflows/build.yml: env: ZMK_REPOSITORY: zmkfirmware/zmk ZMK_REF: main B) If you use a west manifest, set revision: main for zmk: manifest: projects: - name: zmk remote: zmkfirmware revision: main import: app/west.yml Then commit/push, let CI rebuild, and use: &left_layer 1 V with your behavior-layer-tap node. Important: - The argument order is layer, then key (1 V), not V 1. - Flash the same artifact to both halves. Alternative (no version bump needed): - Skip a custom node and just use the built-in: &lt 1 V - Put your tuning in the global hold_tap block so it applies to &lt/&mt: / { behaviors { hold_tap { tapping-term-ms = <250>; quick-tap-ms = <175>; require-prior-idle-ms = <150>; flavor = "balanced"; hold-trigger-key-positions = <...>; hold-trigger-on-release; }; }; } If you paste your workflow (or west.yml), I can point out exactly what to change. Reply or tag me so I see it. For general setup refs: https://zmk.dev/docs/user-setup
Installing ZMK | ZMK Firmware
Unlike other keyboard firmwares, ZMK Firmware has been built from the ground up to allow users to manage
hadronized
hadronizedOP6d ago
with main, this still doesn’t work same error @TyperAI with main
TyperAI
TyperAI6d ago
You’re right to question it—my bad earlier. There is no zmk,behavior-layer-tap binding in ZMK, so CI will keep failing with “lacks binding.” Layer Tap is provided as the built‑in &lt behavior, not as a separate behavior type. Fix plan: 1) Remove the custom left_layer node. 2) In your keymap, bind the key directly as: &lt 1 V 3) Put your hold-tap tuning in the global defaults so it applies to &lt (and &mt): / { behaviors { hold_tap { tapping-term-ms = <250>; quick-tap-ms = <175>; require-prior-idle-ms = <150>; flavor = "balanced"; hold-trigger-key-positions = <39 40 30 18 6 7 8 9 10 11 23 35 34 22 21 19 20 31 32 33 41>; hold-trigger-on-release; }; }; }; Why your original custom node crashed: - You set #binding-cells = <2> and used &left_layer 1 V. - behavior-hold-tap forwards both cells to BOTH children. - &kp only accepts 1 cell; getting 2 leads to undefined behavior/crashes (often on release). After changing: - Rebuild and flash the same artifact to both halves. - If you still see issues, temporarily remap that key to &kp V to verify stability, then reapply &lt 1 V. If you push the updated commit, I can sanity‑check your overlay/keymap. Reply or tag me so I see it.
hadronized
hadronizedOP6d ago
are you kidding me… this is exactly why I think AI is utter shit as always with AI, I will do it myself; have a bad day @TyperAI
TyperAI
TyperAI6d ago
You’re right to be frustrated—sorry for the confusion earlier. Root cause: the custom hold-tap you made forwards two args (1, V) to both children. &lt accepts two, but &kp accepts one. That mismatch causes the crash on release. Safe fix: - In your keymap, replace the custom behavior with the built-in Layer Tap: &lt 1 V - Move your tuning to the global hold_tap defaults so it applies to &lt (and &mt): / { behaviors { hold_tap { tapping-term-ms = <250>; quick-tap-ms = <175>; require-prior-idle-ms = <150>; flavor = "balanced"; hold-trigger-key-positions = <39 40 30 18 6 7 8 9 10 11 23 35 34 22 21 19 20 31 32 33 41>; hold-trigger-on-release; }; }; }; - Rebuild and flash the same artifact to both halves. If it still misbehaves, temporarily set that position to: &kp V to confirm stability, then switch back to: &lt 1 V ZMK docs for reference: - https://zmk.dev/docs/behaviors/hold-tap - https://zmk.dev/docs/features/keymaps If you want, drop a link to your updated commit and tag me—I’ll sanity-check it. If you don’t reply or tag, I won’t get notified.
Keymaps & Behaviors | ZMK Firmware
ZMK uses a declarative approach to keymaps, using devicetree syntax to configure them in a .keymap file.
hadronized
hadronizedOP6d ago
the problem @TyperAI is that I want some keys to have the flavor balanced on &lt, and some prefer-hold, so I do need two modes there…
TyperAI
TyperAI6d ago
Totally doable per-key. Define two custom hold-tap behaviors that implement layer-tap, then use the one you want on each key. Key point: don’t wrap &lt inside hold-tap. Use &mo (momentary layer) for the hold side and &kp for the tap side. That’s the supported pattern. Example: / { behaviors { lt_bal: lt_bal { compatible = "zmk,behavior-hold-tap"; label = "LT_BAL"; #binding-cells = <2>; bindings = <&mo>, <&kp>; // hold = layer, tap = key flavor = "balanced"; tapping-term-ms = <250>; quick-tap-ms = <175>; require-prior-idle-ms = <150>; hold-trigger-key-positions = <39 40 30 18 6 7 8 9 10 11 23 35 34 22 21 19 20 31 32 33 41>; hold-trigger-on-release; }; lt_hold: lt_hold { compatible = "zmk,behavior-hold-tap"; label = "LT_HOLD"; #binding-cells = <2>; bindings = <&mo>, <&kp>; flavor = "hold-preferred"; }; }; }; Usage in the keymap: - Balanced: &lt_bal 1 V - Prefer-hold: &lt_hold 1 S Why your earlier node crashed: it forwarded both args (1, V) to both children; &lt expects two, but &kp expects one. Using &mo + &kp matches arg counts (layer to &mo, key to &kp) and is stable. Notes: - You can keep your hold-trigger-* list if you like; apply it to whichever variant needs it. - The Keymap Editor won’t let you edit those behavior nodes, but it’ll preserve them. Assign “&lt_bal 1 V” or “&lt_hold 1 V” in the keymap. Rebuild and flash the same artifact to both halves. If anything’s still weird, reply/tag me with the commit and I’ll sanity‑check it.
hadronized
hadronizedOP6d ago
@TyperAI you could have suggested the &mo stuff from the beginning haha, but thanks, that should work
TyperAI
TyperAI6d ago
Haha, fair! Sorry for the detour earlier—and glad the &mo pattern fits what you need. Quick recap for per-key flavors: - Define variants that use hold-tap with &mo (hold) and &kp (tap): - lt_bal: flavor = "balanced" - lt_hold: flavor = "hold-preferred" - Keep #binding-cells = <2> and bindings = <&mo>, <&kp> - Use in keymap as: - Balanced: &lt_bal 1 V - Prefer-hold: &lt_hold 1 V - Order is layer, then key Notes: - You can add a third variant (tap-preferred) the same way if needed. - Your hold-trigger-* settings can live on whichever variant(s) you want. - The Keymap Editor won’t surface these nodes in the UI, but raw bindings like “&lt_bal 1 V” will be preserved. - Rebuild and flash the same artifact to both halves. ZMK reference: https://zmk.dev/docs/behaviors/hold-tap If you want me to sanity-check your final overlay/keymap, reply or tag me with a snippet/commit and I’ll take a look.

Did you find this page helpful?