oxc-project/oxc · error
[HIRBuilder] expected block {:?} to exist
Error message
[HIRBuilder] expected block {:?} to exist What it means
Diagnostic from oxlint's vue/valid-define-emits rule (crates/oxc_linter/src/rules/vue/valid_define_emits.rs). `defineEmits` is a compiler macro and may be called exactly once per `<script setup>` block; a second call is reported with both call sites labeled. Only the first call's result is wired to the component by the compiler, so events declared in later calls silently don't exist.
Source
Thrown at crates/oxc_react_compiler/src/react_compiler_lowering/hir_builder.rs:935
visited: &mut FxHashSet<BlockId>,
used: &mut FxHashSet<BlockId>,
used_fallthroughs: &mut FxHashSet<BlockId>,
postorder: &mut Vec<BlockId>,
) {
let was_used = used.contains(&block_id);
let was_visited = visited.contains(&block_id);
visited.insert(block_id);
if is_used {
used.insert(block_id);
}
if was_visited && (was_used || !is_used) {
return;
}
let block = hir
.blocks
.get(&block_id)
.unwrap_or_else(|| panic!("[HIRBuilder] expected block {:?} to exist", block_id));
// Visit successors in reverse order so that when we reverse the
// postorder list, sibling edges come out in program order.
let mut successors = each_terminal_successor(&block.terminal);
successors.reverse();
let fallthrough = terminal_fallthrough(&block.terminal);
// Visit fallthrough first (marking as not-yet-used) to ensure its
// block ID is emitted in the correct position.
if let Some(ft) = fallthrough {
if is_used {
used_fallthroughs.insert(ft);
}
visit(hir, ft, false, visited, used, used_fallthroughs, postorder);
}
for successor in successors {
visit(hir, successor, is_used, visited, used, used_fallthroughs, postorder);View on GitHub (pinned to e1e7af627c)
Solutions
- Merge all events into a single call: `defineEmits({ notify: null, submit: null })` (or one union of call signatures in TS).
- Delete the leftover call after merging.
- Assign once — `const emit = defineEmits([...])` — and reuse `emit` everywhere.
Example fix
// before const emitA = defineEmits(['notify']); const emitB = defineEmits(['submit']); // after const emit = defineEmits(['notify', 'submit']);
Defensive patterns
Strategy: validation
Validate before calling
// only one defineEmits call per <script setup>
const count = (setupSource.match(/\bdefineEmits\s*\(?/g) || []).length;
if (count > 1) throw new Error(`defineEmits called ${count} times; merge the declarations`); Prevention
- Create a single `const emit = defineEmits(...)` at the top of the setup block and reuse it.
- When adding events, extend the existing declaration instead of appending a new call.
- Search for `defineEmits` before merging or copy-pasting setup code.
When it happens
Trigger: Two or more `defineEmits(...)` calls in the same `<script setup>` block — e.g. `defineEmits({ notify: null }); defineEmits({ submit: null });`. The rule records the first call's span and reports every subsequent call alongside it.
Common situations: Merging components or copy-pasting blocks; appending a new `defineEmits` when adding events instead of extending the existing call.
Related errors
- Inline helpers are not supported yet
- TS5042
- TS5081
- invalid fix kind: {s}. Valid fix kinds are fix, suggestion,
- `defineOptions()` cannot be used to declare `{prop_name}`. U
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/c98381a4cfe5b1e2.
Report an issue: GitHub.