fail() sentinel fallback does not cover ok_new==true with no obj/err #53

Closed
opened 2026-08-16 06:28:57 +02:00 by michael · 4 comments
Owner

Problem

fail()'s local sentinel fallback does not fully guarantee a
daw_error/v1-shaped return value.

local ok_new, obj, err = pcall(daw_error.new, _NAME, code, message, opts)
if not ok_new then
  return { schema = "daw_error/v1", id = "daw-error-facility-unavailable", ... }
end
return obj or err

If pcall succeeds (ok_new == true) but daw_error.new() returns
neither obj nor err (e.g. nil, nil), fail() returns nil.
A caller then does return nil, nil, silently violating the "always
return a daw_error/v1 on failure" rule this sentinel path exists to
protect against.

Found while reviewing the daw_i18n/v1 migration of crypto/hash.lua
(DAW/daw-lua-common#48), but the same fail() shape exists in every
file migrated so far (base64.lua, config_parser.lua, crypto/hash.lua)
and likely in further not-yet-migrated files using the same pattern.

Scope

  • Audit all local fail() implementations using this pattern
  • Close the ok_new == true but obj == nil and err == nil gap,
    e.g. falling through to the same sentinel object instead of obj or err
  • Do not change behavior for the already-covered ok_new == false
    (pcall crash) case

Acceptance criteria

  • All affected fail() implementations identified
  • Gap closed consistently across all of them
  • fragjan code . and make test (5.1 and 5.4) pass

DAW/daw-lua-common#48

## Problem `fail()`'s local sentinel fallback does not fully guarantee a daw_error/v1-shaped return value. ```lua local ok_new, obj, err = pcall(daw_error.new, _NAME, code, message, opts) if not ok_new then return { schema = "daw_error/v1", id = "daw-error-facility-unavailable", ... } end return obj or err ``` If `pcall` succeeds (`ok_new == true`) but `daw_error.new()` returns neither `obj` nor `err` (e.g. `nil, nil`), `fail()` returns `nil`. A caller then does `return nil, nil`, silently violating the "always return a daw_error/v1 on failure" rule this sentinel path exists to protect against. Found while reviewing the daw_i18n/v1 migration of crypto/hash.lua (DAW/daw-lua-common#48), but the same `fail()` shape exists in every file migrated so far (base64.lua, config_parser.lua, crypto/hash.lua) and likely in further not-yet-migrated files using the same pattern. ## Scope - Audit all local `fail()` implementations using this pattern - Close the `ok_new == true` but `obj == nil` and `err == nil` gap, e.g. falling through to the same sentinel object instead of `obj or err` - Do not change behavior for the already-covered ok_new == false (pcall crash) case ## Acceptance criteria - [ ] All affected `fail()` implementations identified - [ ] Gap closed consistently across all of them - [ ] fragjan code . and make test (5.1 and 5.4) pass ## Related DAW/daw-lua-common#48
Author
Owner

Also affects crypto/init.lua (same fail()/missing() fallback shape as hash.lua and hmac.lua). No separate issue -- covered by this one.

Also affects crypto/init.lua (same fail()/missing() fallback shape as hash.lua and hmac.lua). No separate issue -- covered by this one.
Author
Owner

Also affects decimal/util.lua, decimal/init.lua, decimal/convert.lua, decimal/compare.lua, decimal/arith.lua (same fail()/no_util()/missing() fallback shape). No separate issue -- covered by this one.

Also affects decimal/util.lua, decimal/init.lua, decimal/convert.lua, decimal/compare.lua, decimal/arith.lua (same fail()/no_util()/missing() fallback shape). No separate issue -- covered by this one.
Author
Owner

Also affects fs/dir.lua's err() (same fail()-shape). No separate issue -- covered by this one.

Also affects fs/dir.lua's err() (same fail()-shape). No separate issue -- covered by this one.
Author
Owner

Done

The gap is closed in all 26 files that used the pattern (base64 as
pilot, then five groups: config_parser/init/sys/time/uuid, crypto/,
decimal/, fs/, json/, yaml/).

  • fail(), err(), no_util() and missing() now fall through to the
    daw-error-facility-unavailable sentinel when daw_error.new() raises
    an error OR succeeds but returns neither an object nor an error.
    data.detail names the no-result case; text and resource_id of the
    sentinel are unchanged.
  • The context of the 26 sentinel resources in i18n/en.json now reads
    '... raised an error or returned no error object'; source_hash in
    i18n/de.json recomputed, no mismatches over all 238 resources.
  • One test per group stubs daw.common.error with a new() returning
    nil. Each was red before the change. In yaml/dump.lua the gap even
    caused a raw Lua error ('attempt to concatenate a nil value'),
    because the caller's 'if err' check saw nil.
  • Verified mechanically: no 'obj or e/err' without the guard is left
    in production code, no old 'if not ok_new then' block remains.
  • make test: 518 tests pass on Lua 5.1 and 5.4; fragjan code . clean.

The same pattern exists in DAW/daw-lua-net (init.lua, ip_utils.lua,
http_client.lua); tracked separately there.

## Done The gap is closed in all 26 files that used the pattern (base64 as pilot, then five groups: config_parser/init/sys/time/uuid, crypto/, decimal/, fs/, json/, yaml/). - fail(), err(), no_util() and missing() now fall through to the daw-error-facility-unavailable sentinel when daw_error.new() raises an error OR succeeds but returns neither an object nor an error. data.detail names the no-result case; text and resource_id of the sentinel are unchanged. - The context of the 26 sentinel resources in i18n/en.json now reads '... raised an error or returned no error object'; source_hash in i18n/de.json recomputed, no mismatches over all 238 resources. - One test per group stubs daw.common.error with a new() returning nil. Each was red before the change. In yaml/dump.lua the gap even caused a raw Lua error ('attempt to concatenate a nil value'), because the caller's 'if err' check saw nil. - Verified mechanically: no 'obj or e/err' without the guard is left in production code, no old 'if not ok_new then' block remains. - make test: 518 tests pass on Lua 5.1 and 5.4; fragjan code . clean. The same pattern exists in DAW/daw-lua-net (init.lua, ip_utils.lua, http_client.lua); tracked separately there.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
DAW/daw-lua-common#53
No description provided.