A for_each whose name list reads a gdal.vect table, and whose expression list then needs another column of that same table, fails in GeoDMS 20.20.0 with an internal check. GeoDMS 20.17.0 runs the same configuration without complaint. The second column is never read.
It turned up in RSopen (pbl-nl/model-RSopen): the TIGRIS claim generation reads a MetaInfo csv whose column Variable gives the names for a for_each and whose column Length gives the field widths used in the expressions. Under 20.20 every claim year failed, and all names the for_each should have made became Unknown identifier. The configuration now works around it (see below), so this is not blocking, but the pattern is ordinary and silent until it hits.
Minimal repro
Two files in one folder. The .dms reads meta.csv through %currDir%.
meta.csv (semicolon separated):
"Variable";"Length"
"key";4
"alpha";3
"beta";3
repro_subitem_for_each.dms:
// Repro: internal check in SubItemOperator::CreateResult (OperMisc.cpp(287)) in GeoDMS 20.20.0,
// where GeoDMS 20.17.0 works.
//
// Files: this .dms and meta.csv (4 lines, semicolon separated) in the same folder.
//
// Run each item in its own process:
// GeoDmsRun.exe /L<log> repro_subitem_for_each.dms /Fails/alpha
// GeoDmsRun.exe /L<log> repro_subitem_for_each.dms /Control/k1
// GeoDmsRun.exe /L<log> repro_subitem_for_each.dms /Workaround/X/alpha
// GeoDmsRun.exe /L<log> repro_subitem_for_each.dms /Canary
//
// Observed (2026-09-24):
// 20.17.0.m 20.20.0.m
// /Fails/alpha exit 0 exit 1, see below
// /Control/k1 exit 0 exit 0
// /Workaround/X/alpha exit 0 exit 0
// /Canary exit 1 exit 1 (deliberate failing IntegrityCheck, proves exit 1 is seen)
//
// 20.20 log for /Fails/alpha:
// [storage read] Read /Meta from .../meta.csv
// [storage read] Read /Meta/Variable from .../meta.csv
// [E] [[/Meta/Length]] Check Failed Error: !mustCalc || !IsDataItem(resultHolder.GetCurrUlt()) || AsDataItem(resultHolder.GetCurrUlt())->m_DataObject || resultHolder->WasFailed(FailType::Data)
// [E] C:\dev\GeoDMS_2026\clc\dll\src\OperMisc.cpp(287): This seems to be a GeoDms internal error
// [E] ErrorLevel up to 1 because the specified item '/Fails/alpha' was not found.
// Column Length is never read. 20.17 reads Meta, Variable and Length as separate tasks.
//
// Pattern: the name list of a for_each reads a gdal.vect table (its row count or one of its columns),
// and the expression list then needs another column of that same table. That column is reached through
// subitem(Meta, 'Length') on a worker thread, after the table was already read without it; the column
// is not read on demand there and the check fails.
// Measured variations on 20.20 (all exit 0 on 20.17):
// - fails also when the name list only uses the row count: for_each_nedv('c'+string(id(Meta)), 'const('+Meta/Length+', void)', ...)
// - fails also when Variable and Length are declared explicitly in the unit, as String or as UInt32
// - fails also when the expression list is a separate attribute on Meta, or reaches the table through an alias unit
// - fails also when the column name is different (Len instead of Length), or when the expression list reads Variable instead of Length
// - works when the table has inline data instead of a storage (nrofrows plus literal attributes)
// - works when the name list does not depend on the table (Control below)
// - works when only the name list reads the table and the expressions are constant
// - works in a plain meta-expression that reads both columns: ='uint32('+first(Meta/Length)+') + strlen('+quote(first(Meta/variable))+')'
// - works when the two columns are requested as two separate top-level items in one run
// - works when the first read of the table already needs both columns (Workaround below)
container repro_subitem_for_each
{
unit<UInt32> Meta
: StorageName = "%currDir%/meta.csv"
, StorageType = "gdal.vect"
, StorageReadOnly = "True";
container Fails := for_each_nedv(Meta/variable, 'const(' + Meta/Length + ', void)', void, uint32);
unit<UInt32> K : nrofrows = 3
{
attribute<String> N : ['k0', 'k1', 'k2'];
attribute<String> E := 'const(' + Meta/Length[convert(id(.), Meta)] + ', void)';
}
container Control := for_each_nedv(K/N, K/E, void, uint32);
container Workaround
{
unit<UInt32> Sel := select_with_org_rel(strlen(Meta/variable) > 0 || Meta/Length[uint32] > 0)
{
attribute<String> name := Meta/variable[org_rel];
attribute<UInt32> Length := Meta/Length[org_rel][uint32];
}
container X := for_each_nedv(Sel/name, 'const(' + string(Sel/Length) + ', void)', void, uint32);
}
parameter<UInt32> Canary := #Meta, IntegrityCheck = "this == 99";
}
Run each item in its own process, with both builds:
"C:\Program Files\ObjectVision\GeoDms20.20.0.m\GeoDmsRun.exe" /Lfails_2020.log repro_subitem_for_each.dms /Fails/alpha
"C:\Program Files\ObjectVision\GeoDms20.17.0.m\GeoDmsRun.exe" /Lfails_2017.log repro_subitem_for_each.dms /Fails/alpha
and the same for /Control/k1, /Workaround/X/alpha and /Canary.
Observed
Rerun on 2026-09-24 with the installed 20.17.0.m and 20.20.0.m builds:
| item |
20.17.0.m |
20.20.0.m |
/Fails/alpha |
exit 0 |
exit 1, internal check |
/Control/k1 |
exit 0 |
exit 0 |
/Workaround/X/alpha |
exit 0 |
exit 0 |
/Canary |
exit 1 |
exit 1 (a deliberately failing IntegrityCheck, to show that exit 1 is seen) |
20.20 log for /Fails/alpha:
[storage read]Read /Meta from <repro>/meta.csv [[/Meta]]
[storage read]Read /Meta/Variable from <repro>/meta.csv [[/Meta]]
[E][[/Meta/Length]] Check Failed Error: !mustCalc || !IsDataItem(resultHolder.GetCurrUlt()) || AsDataItem(resultHolder.GetCurrUlt())->m_DataObject || resultHolder->WasFailed(FailType::Data)
[E]C:\dev\GeoDMS_2026\clc\dll\src\OperMisc.cpp(287):
[E]This seems to be a GeoDms internal error; contact Object Vision or report this as issue at https://github.com/ObjectVision/GeoDMS/issues
[E][[/Fails]] Check Failed Error: (same check)
[E]ErrorLevel up to 1 because the specified item '/Fails/alpha' was not found.
Length is never read. 20.17 read Meta, Variable and Length as separate tasks.
Variations measured on 20.20 (all exit 0 on 20.17)
Fails as well when:
- the name list only uses the row count:
for_each_nedv('c'+string(id(Meta)), 'const('+Meta/Length+', void)', ...)
Variable and Length are declared explicitly in the unit, as String or as UInt32
- the expression list is a separate attribute on
Meta, or reaches the table through an alias unit
- the column has another name (
Len), or the expression list reads Variable instead of Length
Works when:
- the table has inline data instead of a storage (
nrofrows plus literal attributes)
- the name list does not depend on the table (
Control)
- only the name list reads the table and the expressions are constant
- a plain meta-expression reads both columns:
='uint32('+first(Meta/Length)+') + strlen('+quote(first(Meta/variable))+')'
- both columns are requested as two separate top-level items in one run
- the first read of the table already names both columns (
Workaround: a select_with_org_rel whose condition mentions both)
Where to look
The failing check is the MG_CHECK at the end of SubItemOperator::CreateResult in clc/dll/src/OperMisc.cpp: the column is reached as subitem(<table>, 'Length') on a worker thread after the table was read for the name list, and its m_DataObject is still empty. Between v20.17.0 and v20.20.0 the storage reads moved to operator applications (#587: S1 reads a gdal.vect table as an operator application, S4 reads the attributes of a gdal.vect table in one pass over its features, and ef1683c lets a stored member that its table's read does not name read on its own). This case looks like a member that the first read did not name and that is then reached through subitem, where nothing reads it on demand.
Done when
/Fails/alpha gives exit 0 on a build from main, the other three items give the results in the table above, and the repro is added as a regression case under testcases/ (the csv under testcases/data/).
A
for_eachwhose name list reads agdal.vecttable, and whose expression list then needs another column of that same table, fails in GeoDMS 20.20.0 with an internal check. GeoDMS 20.17.0 runs the same configuration without complaint. The second column is never read.It turned up in RSopen (pbl-nl/model-RSopen): the TIGRIS claim generation reads a MetaInfo csv whose column
Variablegives the names for afor_eachand whose columnLengthgives the field widths used in the expressions. Under 20.20 every claim year failed, and all names thefor_eachshould have made becameUnknown identifier. The configuration now works around it (see below), so this is not blocking, but the pattern is ordinary and silent until it hits.Minimal repro
Two files in one folder. The .dms reads
meta.csvthrough%currDir%.meta.csv(semicolon separated):repro_subitem_for_each.dms:Run each item in its own process, with both builds:
and the same for
/Control/k1,/Workaround/X/alphaand/Canary.Observed
Rerun on 2026-09-24 with the installed 20.17.0.m and 20.20.0.m builds:
/Fails/alpha/Control/k1/Workaround/X/alpha/Canary20.20 log for
/Fails/alpha:Lengthis never read. 20.17 readMeta,VariableandLengthas separate tasks.Variations measured on 20.20 (all exit 0 on 20.17)
Fails as well when:
for_each_nedv('c'+string(id(Meta)), 'const('+Meta/Length+', void)', ...)VariableandLengthare declared explicitly in the unit, as String or as UInt32Meta, or reaches the table through an alias unitLen), or the expression list readsVariableinstead ofLengthWorks when:
nrofrowsplus literal attributes)Control)='uint32('+first(Meta/Length)+') + strlen('+quote(first(Meta/variable))+')'Workaround: aselect_with_org_relwhose condition mentions both)Where to look
The failing check is the
MG_CHECKat the end ofSubItemOperator::CreateResultinclc/dll/src/OperMisc.cpp: the column is reached assubitem(<table>, 'Length')on a worker thread after the table was read for the name list, and itsm_DataObjectis still empty. Between v20.17.0 and v20.20.0 the storage reads moved to operator applications (#587: S1 reads a gdal.vect table as an operator application, S4 reads the attributes of a gdal.vect table in one pass over its features, and ef1683c lets a stored member that its table's read does not name read on its own). This case looks like a member that the first read did not name and that is then reached throughsubitem, where nothing reads it on demand.Done when
/Fails/alphagives exit 0 on a build from main, the other three items give the results in the table above, and the repro is added as a regression case undertestcases/(the csv undertestcases/data/).