Skip to content

Fix "That Wasn't Supposed to Happen" on grid Excel export (Group Attendance, Content Channel Item List) - #9

Merged
stphnlee merged 1 commit into
hotfix-1.16.12from
bugfix-jw-GridExportLavaDataObject-v16
Aug 21, 2026
Merged

stphnlee merged 1 commit into
hotfix-1.16.12from
bugfix-jw-GridExportLavaDataObject-v16

Conversation

@jwakefield-secc

Copy link
Copy Markdown

Fixes the That Wasn't Supposed to Happen error page when exporting Group Attendance to Excel.

Problem

Exporting the Group Attendance List grid to Excel fails 100% of the time — every group, every user, every date range, all browsers. Six identical entries in the v16 ExceptionLog (Ids 1573206–1579801, 2026-08-18, page 363 "Group Attendance"):

System.Reflection.TargetParameterCountException: Parameter count mismatch.
   at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
   at Rock.Web.UI.Controls.Grid.Actions_ExcelExportClick(...) in \Rock\Web\UI\Controls\Grid\Grid.cs:line 2705
   at Rock.Web.UI.Controls.GridActions.lbExcelExport_Click(...) in \Rock\Web\UI\Controls\Grid\GridActions.cs:line 694

Grid.cs:2705 is object propValue = prop.GetValue( item, null );.

Root cause

Upstream commit ecef40fd9b ("(Group) Fixed the Group Attendance List block to correctly display custom columns", #5917) changed the grid's row type:

- public class AttendanceListOccurrence
+ public class AttendanceListOccurrence : LavaDataObject

LavaDataObject exposes a public indexer, public object this[string key]. Type.GetProperties() returns it as a property named Item whose getter is non-virtual, so it survives the export's existing property filters. Reading it with GetValue( item, null ) — no index argument — throws.

Grid.FilterDynamicObjectPropertiesCollection exists precisely to strip base-class plumbing properties like this, but only the Fluid branch handles LavaDataObject:

if ( LavaService.RockLiquidIsEnabled )
{
    // DotLiquid.Drop  ✅
    // RockDynamic     ✅
    // LavaDataObject  ❌  <-- missing
}
else
{
    // LavaDataObject  ✅
    // RockDynamic     ✅
}

Our core_LavaEngine_LiquidFramework global attribute is DotLiquid, so RockLiquidIsEnabled == true and the cleanup never runs. Sites on Fluid never hit this, which is why it is unreported upstream.

No upstream fix exists to cherry-pick. Verified against 1.16.13.1 (final v16 release) — the gap is still present. Upstream develop resolved it incidentally in v17 by deleting RockLiquid support, which removed the branch entirely.

Changes

Two files, +20 −1.

1. Grid.cs — skip indexer properties in the export property filter (~2447)

Added as a third guard alongside the existing virtual-property and Data_Lava_ guards. GetIndexParameters() returns an empty array for ordinary properties, so this is a no-op for every other grid. Covers all three reflection reads that consume props.

2. Grid.cs — strip LavaDataObject base properties on the RockLiquid path (~2760)

Copied in shape from the RockDynamic branch immediately above. Brings the RockLiquid branch to parity with the Fluid branch, and matches the end state upstream reached in v17.

This is the load-bearing hunk. FilterDynamicObjectPropertiesCollection has two callers — the Excel export and GetEntitySetFromGridSourceList, which backs Merge Template and Launch Workflow. Hunk 1 only sits in the export loop, so hunk 2 is the only one that repairs the other grid actions.

3. LavaField.cs — exclude indexers from the custom Lava column property dictionary (:246)

PopulateDataItemPropertiesDictionary filtered only virtual getters, so it admitted the Item indexer and read it with GetValue( dataItem, null ) at :226 — meaning any custom Lava column on a LavaDataObject-backed grid threw at page render, on both Lava engines. Same one-line guard. (This is the ironic sibling of SparkDevNetwork#5917: custom columns were the feature the inheritance change was meant to fix.)

What this fixes

Block Export to Excel Merge Template Launch Workflow Custom Lava column
Groups/GroupAttendanceList fixed (confirmed) fixed (traced) n/a — no EntityTypeId, button not rendered fixed (traced)
Cms/ContentChannelItemList fixed (traced) fixed (traced) fixed (traced) fixed (traced)

confirmed = reproduced in the production log. traced = same code path to the same GetValue call, not yet clicked.

Only these two v16 WebForms blocks bind a LavaDataObject-derived list to a Rock:Grid. No SECC plugin type derives from LavaDataObject or RockDynamic. Communicate is not rendered on either block (no PersonIdField).

Behavior change

Hunk 2 removes an AvailableKeys merge field that previously appeared on the EntitySet/merge path for these grids. It is [LavaHidden], carries no meaningful data, and never existed on Fluid — this aligns the two engines. Otherwise the exported column set is identical, minus the Item column that could never be read.

Risk and rollback

  • Low. All three edits are additive and narrow: the indexer guards are inert for non-indexer properties; hunk 2 fires only for LavaDataObject-derived data sources.
  • Hunk 2 filters by property name; checked all 11 LavaDataObject-derived row types in the fork — none declares Item or AvailableKeys.
  • No schema change, no migration, no config change. Rollback is redeploying the previous Rock.dll.

Forward compatibility (v17)

  • Hunk 2 retires itself — the RockLiquidIsEnabled block it lives in was deleted upstream in v17; on merge the deletion wins. A botched conflict resolution leaves an orphaned else if, which is a compile error — loud, not silent.
  • Hunks 1 and 3 persist as fork drift. Upstream develop still lacks both indexer guards. Harmless; record in the fork customization inventory and revisit at the v17 upgrade.

Testing

Not yet built or run — needs a Windows build of Rock.dll. Static analysis against this branch plus the production exception log.

QA checklist once built:

  • Groups/GroupAttendanceList → Export to Excel downloads a file; spot-check row values
  • Groups/GroupAttendanceList → Merge Template proceeds to the merge page
  • Cms/ContentChannelItemList → Export to Excel, Merge Template, Launch Workflow
  • Add a custom Lava column (block settings → Grid Options) to GroupAttendanceList; page renders and the column exports
  • Regression: one unaffected grid (e.g. Groups/GroupMemberList or any report grid) exports unchanged
  • No stray Item column in exported output

Deliberately not in this PR

  • ExportSource="ColumnOutput" workaround in GroupAttendanceList.ascx — viable same-day file-drop unblock, but it has a different deploy/rollback path than a DLL and would muddy a revert. Ship separately if interim relief is wanted.
  • Guest Checkin export failure — different root cause (NullReferenceException at Grid.GetDataSourceObjectType(), null grid DataSource on export postback in org.secc.SportsAndFitness/.../GuestCheckin.ascx.cs). Plugin-side, tracked separately.
  • DotLiquid → Fluid migration — v17 removes RockLiquid; this defect class exists only on the engine branch almost nobody else runs. Deserves its own planned ticket while rollback is still one global attribute.

🤖 Generated with Claude Code

…aObject-backed grids

Rock 16 made AttendanceListOccurrence inherit LavaDataObject (upstream
ecef40f, issue SparkDevNetwork#5917), whose public this[string key] indexer reflects
as a property named Item. The grid's DataSource-mode Excel export reads
every property with prop.GetValue( item, null ) and throws
TargetParameterCountException on the indexer, failing the export on
every group (ExceptionLog 1573206-1579801, 2026-08-18).

FilterDynamicObjectPropertiesCollection already strips LavaDataObject
base properties, but only on the Fluid branch; our RockLiquid
(DotLiquid) engine setting takes the branch that never did. The v16
line ended (1.16.13.1) with the gap still present; v17 removed
RockLiquid entirely, which is why this is unreported upstream.

- Grid.cs: skip indexer properties in the export property filter
- Grid.cs: strip LavaDataObject base properties on the RockLiquid
  branch, mirroring the Fluid branch (also repairs Merge Template and
  Launch Workflow via GetEntitySetFromGridSourceList)
- LavaField.cs: exclude indexer properties when building the custom
  Lava column property dictionary, which otherwise throws at render
  for any custom column on a LavaDataObject-backed grid

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes reflection failures when grids process LavaDataObject indexers during Excel export, merge actions, workflows, and custom Lava rendering.

Changes:

  • Excludes indexed properties from Excel export and Lava field reflection.
  • Aligns RockLiquid property filtering with Fluid behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
Rock/Web/UI/Controls/Grid/Grid.cs Filters indexers and inherited LavaDataObject properties.
Rock/Web/UI/Controls/Grid/LavaField.cs Prevents indexer reads in custom Lava columns.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@stphnlee stphnlee self-assigned this Aug 21, 2026
@stphnlee
stphnlee self-requested a review August 21, 2026 19:34

@stphnlee stphnlee left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed works in dev

@stphnlee
stphnlee merged commit d9c1d3a into hotfix-1.16.12 Aug 21, 2026
1 check 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.

3 participants