Skip to content

Fix ignore_device_delete: TypeError on device leave, option lookup never matched#2935

Merged
asgothian merged 1 commit into
ioBroker:masterfrom
krobipd:fix/ignore-device-delete-crash
Jul 10, 2026
Merged

Fix ignore_device_delete: TypeError on device leave, option lookup never matched#2935
asgothian merged 1 commit into
ioBroker:masterfrom
krobipd:fix/ignore-device-delete-crash

Conversation

@krobipd

@krobipd krobipd commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

The problem

insertedDeviceLeave is the spliced deviceLeave listener that is armed at every controller start. As soon as any device or model has the ignore Delete option set (that is exactly what the optionExists('ignore_device_delete') gate checks), the next deviceLeave for a resolvable device throws instead of ignoring the leave:

  • entity.model does not exist on any resolveEntity() return shape — the device, group and coordinator branches all return {type, device, mapped, endpoint, ...} without a model key. entity.model.modelID is therefore a guaranteed TypeError, raised inside an async event listener → unhandled rejection.

Independently of the crash, the option lookup could never match: getDeviceOption(device_id, model_id, option, dOnly) was called with three arguments, so option was the literal false — the lookup returned undefined, and (undefined ?? {}).ignore_device_delete is always falsy. The feature was inert even where it did not crash.

The fix

if (this.localConfig.getDeviceOption(entity.device.ieeeAddr, entity.mapped?.model, 'ignore_device_delete', true)) {
    this.warn(`Ignoring device_leave message from '${this.devLabel(message.ieeeAddr)}' (${entity.device.modelID})`);
  • the option is read by its real key, with dOnly=true so both the per-device and the per-model option resolve (the option is declared byDevice: true, byModel: true in the catalogue)
  • entity.mapped?.model is the by_model key space the local config stores options under (common.type); ?. keeps unsupported devices safe — getDeviceOption handles an undefined model
  • the log prints the existing entity.device.modelID

Verification

Functional test against the real lib/localConfig.js: the old 3-argument form provably returns undefined for a set option (per-device, stored under .options the way updateConfigItems persists it), the new call resolves the per-device option, the per-model option, and returns undefined for an unsupported device without a mapped model. node --check, ESLint and npm test green.

Note: #2934 touches the same warn line (it adds the model parameter to that devLabel call). Whichever lands second I will rebase promptly — the overlap is that one line.

🤖 Generated with Claude Code

insertedDeviceLeave is the spliced deviceLeave listener that is armed at
every controller start. As soon as any device or model has the
'ignore Delete' option set (that is the optionExists gate), the next
deviceLeave for a resolvable device threw:

- entity.model does not exist on any resolveEntity() return shape
  (device/group/coordinator all return {type, device, mapped, ...}),
  so entity.model.modelID is a TypeError inside an async event
  listener - an unhandled rejection instead of an ignored leave.

Independently, the option lookup itself could never match:
getDeviceOption(device_id, model_id, option, dOnly) was called with
3 arguments, so it looked up the option key `false` and returned
undefined - the leave was never ignored even without the crash.

Now the option is read as getDeviceOption(ieee, entity.mapped?.model,
'ignore_device_delete', true), which resolves the per-device option and
the per-model option (the option is declared byDevice and byModel), and
the log prints the existing entity.device.modelID.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@asgothian asgothian merged commit d109eac into ioBroker:master Jul 10, 2026
5 of 14 checks passed
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.

2 participants