# Known Bad Patterns

Last checked: 2026-07-08

These are mistakes that must not reappear in this guide family.

**Read this before generalizing any entry below into "avoid this whole control."** Every entry here documents ONE specific broken property or pattern on a control, not a blanket verdict on the control itself. A control having one confirmed bug is not evidence that a *different* symptom on that same control shares the same root cause. Before recommending a full control swap because "this control is already known to be broken," re-check that the specific property causing the *current* symptom is the same one named in the entry — if it isn't, diagnose the new symptom fresh, don't reach for the existing fix. (This rule exists because a full `ModernDatePicker` swap got recommended for a one-character `Format` string bug, off the back of an entry below about a completely unrelated `SelectedDate` bug on the same control — see `feedback_minimal_fix_before_destructive` in memory.)

## Blacklisted Controls — Do Not Use, In Any Project

- **`Copilot answer (preview)` modern control.** User directive 2026-07-05: do not
  propose, verify, or insert this control anywhere, in any Power Apps project across
  this whole family — not a "low priority, verify later" item, an outright blacklist.
  Reason given: likely government/organisational cost or licensing tier attached to
  AI/Copilot features at work. Do not suggest it as an option even when a feature
  genuinely sounds like a good fit for it.
- **`Header` modern control (`ModernHeader`).** Verified 2026-07-05 against Microsoft's
  own docs: "Unsupported capabilities: Customization of colors, App navigation." It
  only exposes `Title`/`Logo`/`IsProfilePictureVisible` — no way to add custom action
  buttons (Save/Close/Delete etc.) or nav links. Cannot replace a hand-built action-bar
  top bar. Don't propose it for that purpose again.
- **`Card` modern control (`ModernCard@1.0.0`).** Verified 2026-07-05: fixed,
  non-composable template (`Title`/`Subtitle`/`Description`/`Image` + a single
  `OnSelect` for the *whole* card). Does not support layering independent action
  buttons (edit/delete icons, "Open in App") on top of a full-card click target — the
  pattern every link/dashboard card in this app family actually needs. Don't propose
  it as a replacement for hand-built card patterns.
- **`ModernTabList` (`TabListCanvas`) / Tabs modern control.** User directive
  2026-07-06: blacklisted, do not use in any project. Confirmed broken even by
  Microsoft's own standard — pasting Microsoft's own published example markup for this
  control straight from their docs into the browser editor throws errors, it's not a
  Policy Tracker-specific formula mistake. Don't propose it as a segmented-tab/filter
  replacement for hand-built tab rows.

## Control Properties

- **`GroupContainer@1.5.0` (both `ManualLayout` and `AutoLayout` variants) has `DropShadow` ON by default when the property is left unset.** Not a visible/obvious default — you have to explicitly write `DropShadow: =DropShadow.None` on every single container to turn it off, or every card/row/panel in a whole screen gets an unwanted shadow. Confirmed the hard way on Policy Tracker `ViewItem_1`: every container across the whole screen had a stray drop shadow because this was never set, and it had to be manually stripped from each one after the fact. **Always set `DropShadow: =DropShadow.None` explicitly on every `GroupContainer` unless a shadow is actually wanted for that specific one** (e.g. a modal card popping over a dimmed background) — never rely on the unset/default state being "no shadow."

- Modern Combo box with `Placeholder`. Use `InputTextPlaceholder`.
- Modern Combo box with record-shaped `Items` and no explicit `ItemDisplayText`. Use the field that exists in `Items`, for example `ThisItem.DisplayName` for Office365Users search rows.
- **`ModernCombobox` dropdown panel renders as an empty/"ghost" outline with no list content — after ruling out every formula-level cause.** Confirmed on Policy Tracker `ViewItem_1`'s `cmb_Owner1`: the three invalid properties documented above (`FontWeight: =""`, `Appearance.FilledLighter`, `AllowExternalSelectedItems`) were fixed, `DefaultSelectedItems` correctly pre-selected a real value, and the `Items` formula (`Office365Users.SearchUserV2({...isSearchTermRequired: false...}).value`) already matched every commonly-documented working pattern — yet the dropdown list still opened empty and typing did nothing. **This is a Microsoft-acknowledged platform limitation, not a fixable formula bug**: on an official Power Platform community thread about a Modern dropdown-family control going blank, a Microsoft employee's own reply states verbatim: "Modern controls are not that stable, it displays blank dropdown menu from time to time." **Do not keep iterating on ModernCombobox formulas/properties once the above are ruled out — revert to the Classic `ComboBox@0.0.51` control instead.** That control is not visually "classic-ugly" by default — it supports `BorderRadius` (rounded corners) and `Appearance: ='ComboboxCanvas.Appearance'.FilledLighter`, so it can be styled to match a modern design without the ModernCombobox instability. It needs `ComboBoxDataField@1.5.0` children (set via the Data/"Edit fields" panel, not hand-typed) to define which fields — e.g. `DisplayName`, `Mail` — are searchable/displayed, which `ModernCombobox` doesn't require (`ItemDisplayText` replaces that need on the Modern version). 2026-07-05.
- **`ModernCombobox` (`ComboboxCanvas`) completely unresponsive — can't type in the search box, dropdown arrow does nothing, looks present but dead.** Checked against Microsoft's current official docs property-by-property (not a guess) and found three invalid values causing it: `FontWeight: =""` (only `FontWeight.Bold`/`Semibold`/`Normal`/`Lighter` are valid — bare `""` isn't a real enum value and can stop the control initializing at all), `Appearance: =Appearance.FilledLighter` (not a real enum member — only `Appearance.Filled`, `Appearance.FilledDarker`, `Appearance.Outline` exist), and `AllowExternalSelectedItems` (not a documented property of this control at all). **Fix: delete `FontWeight` and `AllowExternalSelectedItems` entirely (or set `FontWeight` to a real enum value), and change `Appearance` to `Appearance.Filled` or `.FilledDarker`.** Also confirmed while investigating: this control's old `TriggerOutput` property was renamed to `DelayOutput` in a recent Microsoft update (`TriggerOutput = "FocusOut"` → `DelayOutput = true`) — if a paste still has `TriggerOutput`, that's stale, not the unresponsiveness cause itself. Policy Tracker `ViewItem_1`'s `cmb_Owner1`, 2026-07-05.
- Modern Text input with `HintText`. Use `Placeholder`.
- Modern Toggle read as `.Value`. Use `.Checked`.
- Modern Toggle caption set with `Text`. Use `Label`.
- Modern Toggle initial state set with `Default` in this guide family. Use `Checked`.
- Multi-select Combo box preselection with `Default`. Use `DefaultSelectedItems`.
- Modern Dropdown with table-shaped `Items` but no explicit `ItemDisplayText`.
- **`ModernDropdown.Default` built as a hand-constructed `With({_v: ...}, {Value: _v})` literal record — no visible pre-selection on load, even with a real matching value and no PA2108 error.** This is the documented fix for the Value/Value1 collision (see the entry below), and it avoids the paste-time error, but the resulting bare `{Value: "..."}` record apparently isn't always recognized as "the selected item" by the control at render time — the dropdown just shows blank/placeholder despite a legitimate underlying value. Confirmed: Policy Tracker `ViewItem_1`'s `radApproval1` ("Approved For Release By") showed empty on a record that genuinely had an approver set. **Fix: use `LookUp(Items, Value = targetValue)` instead of constructing the record by hand** — e.g. `Default: =LookUp(Choices('List'.'Field'), Value = Coalesce(varRecord.'Field'.Value, "fallback"))`. This returns the actual matching row object from `Items`, guaranteeing structural equality rather than a look-alike literal. 2026-07-05.
  - **Escalates to a hard paste-time error (not just a silent blank), specifically when `Items` is a manually-built collection from an inline scalar array** (e.g. `ClearCollect(colOptions, ["A", "B", "C"])`, done to dodge the Value/Value1 duplicate-literal collision — see the entry below). In that case a hand-constructed `Default: ={Value: "A"}` *or* a raw source field passed straight through (`Default: =ThisItem.ChoiceField`, or even `.ChoiceField.Value`) both throw **"invalid formula, expected a value compatible with Items"** at paste time, and any `OnSelect` handler reading `.Selected.Value` from that broken dropdown then throws a second, confusing downstream error (`Patch` failing with "incompatible type... expects a text type, and you're using an error type") — the second error is just the broken dropdown's `Selected` propagating as an error type into whatever consumes it, not a separate bug. The array-literal's column type isn't structurally identical to a hand-built record even though both look like `{Value: Text}` — same underlying cause as the blank-on-load symptom above, just a stricter failure. **Fix is the same trick, applied on both sides:** `Default: =LookUp(colOptions, Value = ThisItem.ChoiceField.Value)` (or `= "A"` for a fixed default) — always derive `Default` from a row pulled out of the exact same collection `Items` points at, never a value passed straight from the data source and never a hand-typed record literal, even when `Items` itself is just a local collection you built. Confirmed live: Branch Contact Groups `scrManageGroups`, `drpEditThursdayBatch`/`drpNewThursdayBatch` against `colThursdayBatchOptions`, 2026-07-24.
- Modern Dropdown `Default` set as a plain string. Must be a record: `{Value: “...”}`. A plain string causes the dropdown to appear blank on load.
- Any property copied from a similar control without checking that control's Microsoft Learn page.
- `Size` on `Toggle@1.1.5`. Toggle does not expose font-size. Confirmed PA2108 in live editor 2026-06-20.
- `FontWeight` on `ModernButton@1.0.0`. Modern Button does not expose FontWeight. Confirmed by cross-check against user's working Screens.txt (which uses BasePaletteColor, Color, Size but never FontWeight) 2026-06-20.
- Radius properties (`RadiusTopLeft`, `RadiusTopRight`, `RadiusBottomLeft`, `RadiusBottomRight`) on **`Label@2.5.1` OR `Rectangle@2.3.0`** — neither exposes them. Confirmed PA2108 twice: Label 2026-06-23, and again on both Label and Rectangle 2026-07-04 (Policy Tracker Main_1 issue #7 — repeated the exact same mistake the Bible already documented, on nine separate controls in one file, because the file wasn't re-checked before writing). Verified alternatives, both confirmed real: **`Classic/Button@2.2.0`** (`DisplayMode: =DisplayMode.View`, `Text: =""`, radius on the button itself — the original documented fix, best for pure shapes/dots with no text) or **`ModernText@1.0.0`** (supports `RadiusTopLeft` etc. natively per Microsoft's modern-control docs, and already used live in this exact app's `Main.yaml` for pill-style badges — best when the element is text-bearing, e.g. a coloured pill label). **Before writing ANY new screen YAML with rounded corners on something that looks like a Label or Rectangle, grep this exact rule first — don't re-derive it from scratch or guess.**
- Light or pastel `BasePaletteColor` on `ModernButton`. Fluent 2 overrides it with a dark fill. Use dark, saturated colours only.
- Conditional `BasePaletteColor` on `ModernButton` (e.g. `If(varTab="X", lightColour, darkColour)`). The light state always renders dark. Use Classic Button for any conditional colour states.
- `Color` override on a `ModernButton` to compensate for a wrong `BasePaletteColor`. Pick a darker seed instead.
- Trying to match an exact brand hex on a `ModernButton`. Work with Fluent 2 — pick the closest dark seed and let the framework derive the final shade.
- `Text: =` (bare, nothing after the equals) in YAML. Must be `Text: =””`.
- **Bare `PropName: =` (nothing after the equals) is not unique to `Text` — it breaks the same way on ANY property, and it recurs on third-party/external component YAML, not just hand-authored screens.** Confirmed 2026-07-25 auditing 23 components pasted from powerappsui.com: 13 of 23 files had at least one bare `Default: =` on a component's `PropertyKind: Event` custom-property definition (e.g. `OnBarClick`, `OnSave`, `OnRowSelect`), 2 files had a bare `OnSelect: =` on an actual `Gallery@2.15.0` control instance, and several had bare `LayoutMaxHeight`/`LayoutMaxWidth`/`Height`/`Width` on real controls. A generic YAML parser (PyYAML) chokes on all of these with `could not determine a constructor for the tag 'tag:yaml.org,2002:value'` — a bare `=` is YAML 1.1's reserved "default value" tag, not a normal scalar — so this is a genuine syntax defect, not just a Power Apps quirk, and it will very likely fail paste in Studio the same way `Text: =` does. **Before trusting any pasted-in component YAML (from a library, a doc site, an export, anything not hand-typed against this Bible), grep it for `: =\s*$` (property name, colon, equals, then nothing) across every property, not just `Text`.** Fix depends on the property: Event `Default` → `=false` (inert placeholder, Events don't need a meaningful default); a real behavior formula like `OnSelect` on a control instance → `=false` (no-op) or the actual intended formula if recoverable; a sizing property (`Width`/`Height`/`LayoutMaxWidth`/`LayoutMaxHeight`) → check whether `AlignInContainer: =AlignInContainer.Stretch` or `AutoHeight: =true` already governs that axis (if so, just delete the bare property, it's redundant) — otherwise DO NOT guess a pixel number; deleting the property (letting the axis go uncapped/unset) is safer than fabricating a value, since omission is normally a valid, common state for these properties elsewhere in the same file. Full detail: `external/powerappsui/README.md` and each affected component's own "Bible Audit" section.
- **`LayoutOverflowY` on a ManualLayout GroupContainer** causes PA2108 (“Unknown property 'LayoutOverflowY' for control type 'GroupContainer@1.5.0' and variant 'ManualLayout'”). `LayoutOverflowY` is only valid on `Variant: AutoLayout` containers. If you need a scrollable region in a ManualLayout screen, wrap the scrollable content in an inner AutoLayout GroupContainer with `LayoutDirection: Vertical` and `LayoutOverflowY: =LayoutOverflow.Scroll`. Confirmed: ViewItem V2.1 conForm (495,13).
- **Any YAML property value that contains `: ` (colon-space) must use a `|-` block scalar.** If a formula like `=UpdateContext({locFoo: true})` is written inline, YAML parses the `{locFoo: true}` as a mapping and throws `YamlInvalidSyntax: While scanning a plain scalar value, found invalid mapping`. This applies to any inline formula containing `{key: value}`, record literals, or any colon-space sequence. Always use `|-` for these. Confirmed: ViewItem V2.1, `OnSelect: =UpdateContext({locDisplayDeletePopUp: true})` at (363,66).
- **`HtmlText` (and any non-event-handler property) with inline CSS or other `: ` in the string literal.** `|-` is NOT allowed on `HtmlText` (PA1001) — so the `|-` rule above cannot apply. Instead, wrap the entire YAML value in **single quotes** and escape any literal single quotes inside by doubling them (`''`). Example: `HtmlText: '="<div style=""border: 1px solid red;"">..."'` where a `'Open Sans'` font reference becomes `''Open Sans''`. The YAML parser then treats the content as a quoted scalar and never sees the `: ` as a mapping separator. Confirmed: ViewItem V3.5, `HtmlText` with CSS properties (`border: 1px`, `padding: 14px`, `font-family: 'Open Sans'`) at (1205,56).
- `%QUALIFIED_DATACARD_FIELD_VALUE.ID%` in a Form card `Default`. This is a broken placeholder — replace with the actual field expression, e.g. `ThisItem.DateCompleted`.
- YAML properties on a control generated from the guide's *intent* without running an audit against `verified-control-reference.md`. Always audit before publishing.
- **`FillPortions: =1` (or any value >0) on a fixed-height container inside a `Variant: AutoLayout` parent — the "Flexible height" toggle in the property panel.** A child with `FillPortions > 0` grows to claim a share of the parent's *remaining* main-axis space, ignoring its own `Height` property entirely — this is the AutoLayout equivalent of CSS flex-grow, not a hint. Confirmed: Policy Tracker `ViewItem_1` — `conTopBar1`, `conWarningBar1`, `conCompactStepper1` and other fixed-height (64/64/44px) containers rendered many times taller than authored because "Flexible height" was ticked (`FillPortions: =1`) instead of off (`FillPortions: =0`). **Symptom: a ManualLayout or AutoLayout container with an explicit `Height` renders drastically oversized when placed inside a vertical `AutoLayout` stack, while sibling containers with actual `FillPortions: =1` correctly expand to fill remaining space.** Fix: set `FillPortions: =0` (or omit the property, if `0` is the default in this control version) on every container that must hold to its authored fixed `Height`; reserve `FillPortions: =1`+ only for the one container genuinely meant to stretch and fill the leftover space (e.g. a scrollable body region below a fixed header). **Do not confuse this with `AlignInContainer: Stretch`** — that only affects cross-axis sizing (width, in a vertical layout) and was tried first as a fix here; it didn't address the actual bug, which was the main-axis (height) `FillPortions` setting. Check `FillPortions` before `AlignInContainer` when a container renders the wrong *height* in an AutoLayout stack. 2026-07-05.

## Power Fx

- `ScreenTransition.Back`. Use `Back()` or a valid `ScreenTransition` enum.
- **Strict `= false` checks on SharePoint Yes/No fields inside filters can exclude blank/unticked rows.** Confirmed live on Policy Tracker `NewsletterPack`, 2026-08-13: two newsletter items had the right Thursday date, real ACOS approval, and `Published = false`, but did not enter `colNewsletterPack` because the consultation Yes/No field failed the strict check `'External consultation required' = false`. The business rule was still correct — consultation-ticked items must stay out — but the formula needed to treat blank as unticked. Use `Coalesce('External consultation required', false) = false` for SharePoint Yes/No gates where blank should count as false. Do not "fix" this by removing the gate unless the user explicitly changes the business rule.
- **Relying on `Navigate()` ordering to prevent one-frame UI flashes while clearing old-screen state.** Confirmed live on Policy Tracker `ViewItem`, 2026-08-13: moving `Navigate(Main, ...)` before `Set(varSelectPolicy, "Empty")` in `btnClose1.OnSelect` did not stop the amber 10-day warning banner flashing, because Power Apps can still evaluate the old screen while statements after `Navigate()` run. Fix: add an explicit close/navigation guard such as `varClosingViewItem`; set it `true` before clearing state or navigating on every close/discard path; add `!Coalesce(varClosingViewItem, false)` to any warning/header visibility formulas that must stay stable during exit; reset the guard in the destination/opening screen's `OnVisible`. Do not treat statement order around `Navigate()` as a visual synchronization guarantee.
- **Immediate `Navigate(...)` guard inside a screen's own `OnVisible`.** Confirmed on Training Tracker `scrAdminDash`, 2026-07-29: `If(!varIsAdmin, Navigate(scrMyTraining, ScreenTransition.None));` in `scrAdminDash.OnVisible` produced Studio's error that `Navigate` cannot be used there because it would automatically always navigate away from the screen. Fix: remove the immediate `OnVisible` navigation line and guard the entry point instead, for example make the Admin button visible only when `varIsAdmin` is true. If a fallback redirect is still needed, use a delayed pattern only after testing in the live editor.
- **`Set(varName, Blank())` as the only or first-encountered assignment for a global variable.** Throws `No type found for variable 'varName'. Ensure that it is Set to a non-Blank value somewhere in the app` — even if the same variable genuinely is set to a real typed value elsewhere (e.g. `Set(varName, someTextExpression)`). Power Fx's type inference isn't reliably finding the typed assignment first. **Fix: replace `Blank()` with a properly-typed empty value that matches the real assignment** — `""` for Text, not `Blank()`, at every call site for that variable (reset/clear points, not just the first one). Confirmed: Policy Tracker `ViewItem_1`'s `varLinkDisplayNamePrefill` (also set to `varFileName`, a Text value, elsewhere) — 6 separate `Set(..., Blank())` call sites all needed changing to `Set(..., "")`. 2026-07-05.
- Bare `Ascending` or `Descending`. Use `SortOrder.Ascending` or `SortOrder.Descending`.
- Bare `Days`, `Months`, `Years` etc. or `TimeUnit.Days` as the unit argument to `DateAdd`/`DateDiff`. **Always use quoted strings: `”Days”`, `”Months”`, `”Years”`.** Confirmed broken in live builds (Training Tracker + Policy Tracker + Branch Contact Groups — recurred here despite already being documented, so double-check this specific rule every time `DateDiff`/`DateAdd` is written, not just on first use).
- Schema-less `[]` where Power Apps must infer collection shape.
- `AddColumns(table, "ColumnName", formula)` with a **quoted string** for the new column name argument. **The column name must be a bare identifier: `AddColumns(table, ColumnName, formula)`.** This rule applies only to the column name — other string arguments in the same formula (e.g. `"Days"` in `DateAdd`) remain quoted as normal. Confirmed in Overview build. **The same rule applies to `GroupBy(table, "ColName", "GroupColName")`** — both column-name arguments must be bare identifiers too (`GroupBy(table, ColName, GroupColName)`). Confirmed broken in Branch Contact Groups (`colPeople` build, Thursday-batch grouping) — quoting them didn't error at paste time, it silently produced a table without the expected field names, breaking every downstream reference with "Name isn't recognized"/"Email isn't recognized" style errors far from the actual bug. 2026-07-23.
- **`LookUp(table, condition1, condition2)` with more than one comma-separated condition.** Unlike `Filter`, `LookUp`'s signature is `LookUp(Table, Formula, [ResultColumnOrReducerFormula])` — a second comma-separated argument is NOT ANDed with the first, it's evaluated as the ResultColumn/reducer formula for the matched row. If that second argument is itself a boolean comparison (e.g. `Lower(Email) = Lower(varEmail)`), `LookUp` returns a **Boolean**, not the matched record — silently, with no error at the `LookUp` call site itself. The error only surfaces later, at every place the caller tries to treat the result as a record (`.Title` on it throws "The dot operator cannot be used on Boolean values"; passing it to `Remove()`/`Patch()` throws "cannot use a non-record value in this context" / "incompatible type"). **Fix: combine all conditions into one predicate with `&&` — `LookUp(table, condition1 && condition2)`.** Confirmed in Branch Contact Groups `scrManagePeople` Save-changes handler (`existing` variable). 2026-07-23.
- Gallery row index formulas. Use `ThisItem.ItemIndex` — it shifts when the gallery filters or sorts.
- `ThisItem.ID` inside collection-backed galleries unless the collection explicitly contains `ID`.
- **`Switch()`/`If()` used *inside* a `Filter()`'s predicate against a SharePoint data source — silently breaks delegation and gives wrong/inconsistent results, even though the identical set of conditions works fine when `Switch` instead picks between whole separate `Filter()` calls.** This app's own `HeaderGallery.Items` already does it the safe way (`Switch(varSelectedState, "Current", Sort(...Filter('Policy Proof Tracker', conditionA)...), "Future", Sort(...Filter(..., conditionB)...), ...)` — one plain-boolean `Filter` per branch, `Switch` only ever chooses which whole expression to run). A per-chip live-count formula instead put `Switch(varSelectedState, "Current", condA, "Future", condB, ...) && ownerCond && typeCond` as the single predicate passed to one `Filter()` call — same conditions, wrong shape. Confirmed live on Policy Tracker `Main`: chip counts summed to 25 with the "Issues" tab selected, while that tab's own real item count was 0, which is only possible if the count formula wasn't actually gating on the tab at all. **Fix: never nest `Switch`/`If` inside a single `Filter`'s predicate against a delegable data source — expand it inline as `(state = "X" && condA) || (state = "Y" && condB) || ...` instead**, matching the shape every other working `Filter` in this app already uses. 2026-07-06.
- Nested formulas that rely on ambiguous `ThisItem`; capture the row with `With` or use `As` aliases.
- **Referencing an outer `AddColumns` row identifier (e.g. `Value` from `Sequence`) inside a nested `Filter` predicate.** Inside `Filter(innerTable, predicate)`, the predicate is evaluated with `innerTable`'s row scope. If `innerTable` doesn't have a `Value` column the identifier falls through, but if there's any ambiguity PowerApps resolves it to blank — making `DateAdd(varBase, (Value-1)*7, "Days")` evaluate to the same date for every row. **Always use `With` to capture outer-scope identifiers before entering the Filter:** `With({weekDate: DateAdd(varBase, (Value-1)*7, "Days")}, Filter(innerTable, DateField = weekDate))`. Confirmed: all 4 Overview columns showed identical data because `Value` inside the nested Filter was blank, computing the same date for all 4 columns. 2026-06-25.
- **A nested Gallery's `Items` property cannot evaluate the outer gallery's `ThisItem`.** The `Items` formula of a child gallery is evaluated at screen scope, not template scope — so `ThisItem.Column` in `galWeekItems.Items` is always blank regardless of whether the gallery is inside a GroupContainer or a direct child. Labels and other non-gallery controls DO correctly resolve `ThisItem` in their properties. **The correct pattern: pre-compute the inner data as a column in the outer gallery's `Items` formula using `AddColumns`, then set the inner gallery's `Items = ThisItem.PreComputedColumn`.** This is the same pattern as `galHeaders → ThisItem.GroupedItems` in Main.yaml. Always collect the source data into a local collection first (`ClearCollect(colName, Filter(...))`) so the `Filter` inside `AddColumns` runs client-side. Confirmed: all four failure modes tested in Policy Tracker Overview 2026-06-24/25.
- Bare `DisplayMode=View`. Use `DisplayMode=DisplayMode.View` or set the property value to `DisplayMode.View`.
- **`DisplayMode: =DisplayMode.View` on a `Classic/Button` that also has a real `OnSelect` — no paste-time error, no visual clue, the tap just silently does nothing.** Per Microsoft's own docs, View mode means the control "cannot... otherwise interact with it" — for a Button that disables the click entirely, not just its hover/pressed styling. This app family has a long-standing convention of setting `DisplayMode.View` on invisible/decorative Buttons used as tap-target overlays or coloured backgrounds (to suppress the default blue hover/press flash) — that's fine and correct for controls with no `OnSelect` (`recCardFrame`, `recHeader`, `recCreatePanelBg`, etc.), but it silently kills the control for the ones that DO have `OnSelect`. Confirmed live 2026-07-24 on Branch Contact Groups: `recResultTap` (GAL search result row — user reported "clicking on it does nothing"), plus 3 more found by grepping the same pattern before the user hit them one at a time: `recCheckBox` (scrSendEmail), `recPersonRowFrame` (scrManagePeople), and `recLightboxBackdrop` on all three screens (click-outside-to-close). **Fix: never combine `DisplayMode: =DisplayMode.View` with `OnSelect` on the same Button** — just omit the `DisplayMode` line entirely; `Fill`/`HoverFill`/`PressedFill` alone already give full control over the visual appearance without disabling the tap. **Before shipping any new invisible-overlay-button pattern, grep the screen for `DisplayMode.View` and `OnSelect` both appearing on the same control** — this is exactly the kind of bug that hides for a long time because it never throws an error and the control still renders correctly, it just doesn't respond to the one interaction that matters.
  - **Correction, 2026-07-24 same incident, `recResultTap`/`recRowFrame`:** a follow-up scrub can still miss one if the `OnSelect` was added to the control *after* the original scrub ran (which is exactly what happened here — `recRowFrame` had no `OnSelect` at scrub time, so the grep for "View + OnSelect" didn't catch it; `OnSelect` was added in a later pass without re-checking for a leftover `DisplayMode.View`). **Whenever adding a new `OnSelect` to an existing control, always check/remove any pre-existing `DisplayMode.View` on it in the same edit** — don't treat "add OnSelect" and "remove stale DisplayMode" as two separate, independently-forgettable steps.
- **A lightbox's backdrop/panel Buttons declared AFTER their own content controls in the children list — the panel's opaque Fill renders in front of everything, so the lightbox appears completely empty** (just the title and close icon, if those happen to be declared even later than the panel). Z-order in this app's YAML is simple list order — later children render in front of earlier ones — so a lightbox's `recLightboxBackdrop`/`recLightboxPanel` must be declared BEFORE all of that lightbox's content controls, not after. Confirmed live 2026-07-24 on Branch Contact Groups `scrManagePeople`: the backdrop/panel/title/close block sat near the very end of the children list (after Add Person, Edit Person, and Purge fields), so every one of those fields was hidden behind the opaque white panel — only the title and close icon (declared even later, so in front of the panel too) were visible. This was a pre-existing bug from before that day's changes, not introduced by whatever was being added at the time — it simply hadn't been exercised/noticed yet. `scrManageGroups` and `scrSendEmail` already had the correct order (backdrop/panel/title/close declared first, all content after) from the start, which is why they never showed this symptom. **When building or auditing any lightbox, check that `recLightboxBackdrop`/`recLightboxPanel` are the FIRST children declared for that lightbox, not the last** — an empty-looking lightbox (title/close visible, everything else blank) is the exact signature of this bug.
- **A row's click-catcher Button sitting in front of its own text Labels, with a real (non-transparent) `HoverFill` set for a hover highlight — the text visibly disappears on hover, replaced by a solid-coloured rectangle.** If a Button needs to both catch clicks (so it must sit in front of overlapping Labels, per the entry below) and show a hover highlight, combining both jobs into one control is a trap: its opaque `HoverFill` renders on top of the Labels the instant the mouse moves over it, since it's in front of them. This isn't a colour-contrast problem — the text isn't just hard to read, it's fully covered. Confirmed live 2026-07-24 on Branch Contact Groups: `recResultTap` (GAL search result row, scrManageGroups) had `HoverFill: =RGBA(233,241,246,1)` and sat after `lblResultName`/`lblResultEmail` in the children list (i.e. in front of them) — hovering any result row made its name and email vanish completely under the pale blue rectangle. **Fix: split hover-highlight and click-catching into two separate controls** — a decorative highlight rectangle at the very back (`DisplayMode.View`, no `OnSelect`, its `HoverFill` is the real highlight colour) sitting *behind* the Labels, and a separate, fully transparent click-catcher (`HoverFill: =Self.Fill`, i.e. never visually changes) sitting *in front of* the Labels holding the real `OnSelect`. This is the same "background → labels → transparent click-catcher → distinct-action controls" ordering already established for click-passthrough — just add the highlight rectangle as a fourth layer at the very back, before everything else.
- **A background click-target Button under overlapping Labels — clicks only register on the blank margins, not on the visible text itself.** Once a full-size background Button has a working `OnSelect` (see entry above), any Label layered on top of it for visible text (name, meta line, notes, etc.) still blocks the tap from reaching it — a control fully occupies its own space for hit-testing regardless of whether it has its own `OnSelect`, so Labels do not let clicks pass through to whatever's behind them. **Don't fix this by duplicating the same `OnSelect` onto every overlapping Label** — fragile, easy to miss one (tried and explicitly rejected by the user on Branch Contact Groups 2026-07-24: "I can't believe there's no smart way around this"). **Correct fix: add ONE separate, fully transparent Button (`Fill: =RGBA(0,0,0,0)`, `BorderThickness: =0`, no text) positioned in front of the Labels but behind any small control that needs its own distinct action (a checkbox, an icon)** — transparent, so nothing is visually hidden; in front of the Labels, so it catches every tap regardless of where the text sits; behind the small distinct-action controls, so they keep working independently. Z-order in this app family's YAML is simple ordering — later children render in front of earlier ones — so the required order is: decorative background → Labels → transparent click-catcher → any small distinct-action controls last. Confirmed live: Branch Contact Groups `recRowClickCatcher` (scrSendEmail, sits in front of `recRowFrame`/labels, behind `recCheckBox`/`icoViewMembers`/`icoViewHistory`) and `recPersonRowClickCatcher` (scrManagePeople, no other distinct-action controls in that row so it's simply frontmost).
- `\xB7` or any `\x` escape sequence in Power Apps strings. Use `Char(decimal)` instead. Middle dot is `Char(183)`.
- `IsOdd()` — does not exist in Power Apps. Use `Mod(value, 2) = 1`.
- `SortByColumns` on a SharePoint Choice field. Use `Sort(..., Field.Value, SortOrder.Ascending)` to unwrap the Choice record.
- `”value” in MultiChoiceField` for multi-select Choice filtering. Use `CountIf(MultiChoiceField, Value = “value”) > 0`.
- `Launch(url)` without `LaunchTarget.New` — reuses current tab. Use `Launch(url, {}, LaunchTarget.New)`.
- `ScrollTo(gallery, item)` in screen `OnVisible` or any behavior formula. **`ScrollTo` is not universally supported in canvas apps** — confirmed "unknown or unsupported function" in live build (Policy Tracker). Remove it entirely; restore scroll position via Navigate to the screen instead, or accept the gallery always resets to the top.
- `SortByColumns(AddColumns(table, "SortKey", formula), "SortKey", ...)` pattern inside a **nested gallery's `Items` property** (where `table = ThisItem.GroupedItems`). The dynamically added column is not reliably resolved by `SortByColumns` in this context — confirmed error "column 'SortKey' does not exist" (Policy Tracker). Use `Sort(ThisItem.GroupedItems, formula, SortOrder.Ascending)` instead, referencing fields directly by bare name inside the formula.
- Referencing a SharePoint field in YAML that has not been confirmed to exist in the list. Before using any field name in card/form YAML, verify it is in the live SP list. Confirmed error when `Status.Value` was used but the SP list only had `Published` (boolean) — Policy Tracker.
- `ForAll(cmbControl.SelectedItems As T, {Value: T.Value})` for patching multi-select Choice. Prefer `ForAll(cmbControl.SelectedItems, {Value: Value})` — the alias form can cause type mismatches.
- **Assuming `Default`/`DefaultSelectedItems` re-evaluates reactively when the source variable changes after the control already exists.** It doesn't — confirmed via Microsoft's own `Reset()` function docs and multiple community threads: these properties are read once, at control creation, to seed the initial value. If a screen sets `varSelectedRecord` (or similar) in `OnVisible` and expects an already-rendered dropdown/combobox/checkbox to pick it up, it won't — the control was created before (or in the same tick as) the variable being set, so it captured whatever value existed at that moment (often blank). **Fix: explicitly `Reset(controlName)` for every Default-driven input, at the very end of `OnVisible`, after all the data-loading Sets/ClearCollects have run.** This applies to `ModernDropdown.Default`, `ComboBox.DefaultSelectedItems`, `Classic/CheckBox.Default`, `DatePicker.SelectedDate` when treated as an initial value, etc. — not just Modern controls. Confirmed: Policy Tracker `ViewItem_1` — `drpDocType1`, `radApproval1`, `cmb_Owner1` all showed blank/unselected on a record with real data, even after fixing the `Default` formula itself (see the `LookUp` entry above) — the formula was correct, the control just never re-read it. 2026-07-05.
  - **Same root cause, different shape — it also happens INSIDE a Gallery, per-row, not just for a single shared control.** A Gallery can recycle its child control instances across an `Items` refresh (e.g. `ClearCollect`) rather than fully destroying and recreating them, especially when the row count doesn't change — so a checkbox's `Default` bound to `ThisItem.Picked` (or similar) can silently keep showing whatever it captured the FIRST time that row-instance was created, even though the underlying collection now has genuinely different `Picked` values for a newly-selected record. Symptom: the checklist visually renders correctly (right group names, right count) but nothing is ever ticked, no matter what the real membership data says — easy to misdiagnose as "the box is too small, the ticked ones are scrolled out of view" (a wrong theory tried first here) since the visible SYMPTOM looks identical to a scroll-visibility problem. Confirmed live 2026-07-24, Branch Contact Groups `scrManagePeople` `chkGroupMember`/`colPersonGroupPicks` — persisted even after enlarging the checklist from 180px to 320px, which is what disproved the scroll theory and pointed at this instead.
    - **First attempted fix, also wrong, and worth recording because it produces a distinctive visible error banner: `Reset(controlName)` called from OUTSIDE the gallery that control lives in.** This is not legal Power Fx — a gallery template's child controls can only be `Reset()` from within a formula written inside that same gallery template, never from a button/OnSelect elsewhere on the screen. The runtime error is exactly this text: *"The Reset function can only be used with a resettable control. Controls within a Form or Gallery control can only be reset from within the same form or gallery template."* If you see that banner, the immediate cause is always a `Reset()` targeting a gallery-child control from the wrong scope — remove it, don't try to relocate it into a different outside caller, it can't legally be called from anywhere outside that gallery.
    - **Real, confirmed fix: convert the checkbox from `Classic/CheckBox@2.1.0` (one-shot `Default`) to `CheckBox@0.0.30`, the Modern Checkbox, using `Checked` instead of `Default`.** `Checked` is genuinely reactive — it re-evaluates on every render, the same way any other Modern-control formula-bound property does (`Visible`, `Text`, `BasePaletteColor`, etc. all already behave this way elsewhere in this app family) — so it correctly reflects the collection's current `Picked` value for whichever record is now selected, with no `Reset()` of any kind needed anywhere. This sidesteps the whole "does Default re-read" question entirely rather than fighting it. Confirmed live 2026-07-24 same incident, `chkGroupMember`/`chkAddPersonGroup`.
  - **EXCEPTION — do not call `Reset()` on `ModernCombobox`.** Microsoft's own changelog for this control states: "Reset() includes SearchText: The Reset() function now clears both the selection and the search text." For this control specifically, `Reset()` was changed to *also* blank the selection — it does not just restore `DefaultSelectedItems` the way `Reset()` does for every other control. Confirmed: Policy Tracker `ViewItem_1`'s `cmb_Owner1` had a correctly-computed `DefaultSelectedItems` (set earlier in the same `OnVisible`, in the right order) that was then immediately wiped back to blank by the screen's own `Reset(cmb_Owner1);` call, applying the general advice above. **Fix: skip `Reset()` for `ModernCombobox` controls entirely** — if `DefaultSelectedItems` is set before the control is created (i.e., earlier in the same `OnVisible`/before first render), it doesn't need a `Reset()` nudge the way `Default`-driven controls do. 2026-07-05.
- Notify-first validation pattern (checking fields then calling Notify before running Patch). Use Set-error-vars-then-If-guard pattern instead.
- **Calling `Reset(Self)` inside a control's own `OnChange`, when that control's value property is itself bound to a live formula (e.g. `DatePicker.SelectedDate: =varSelectedRecord.Field`).** `Reset()` forces the control to re-run its bound formula immediately — which just puts back the old/blank value, silently destroying whatever the user just picked. This was being used as an ad-hoc validation trick ("if the picked date isn't a Thursday, reset it") but it fires on every non-matching pick, not just invalid saves, so it reads as "the field randomly clears itself" rather than a deterministic rule. Confirmed on Policy Tracker `ViewItem`'s `dpkPublishDate1`: any non-Thursday click silently reverted to `varSelectedRelease.'Planned Publish Date'`. **Fix: never `Reset(Self)` for input validation on a formula-bound value control. Just `Notify()` the warning (the invalid value stays visible/selected) and enforce the real rule at Save time instead**, e.g. gate the Save button's validation on `Weekday(control.SelectedDate, StartOfWeek.Monday) <> 4` so a bad value blocks the save rather than getting silently erased before the user even reaches Save. 2026-07-06.
- **`Set()` on a global variable from inside `ForAll` — Studio now blocks this outright: "This function cannot be invoked within ForAll."** This is a real, current Power Apps platform restriction, not a formula mistake — `ForAll` iterations aren't guaranteed to execute in strict sequence, so Microsoft disallows mutating global state from inside the loop to avoid race conditions. This most often shows up in an accumulator pattern, e.g. counting failures from a per-row `IfError` inside a bulk `ForAll(Patch(...))` loop: `IfError(Patch(...), Set(varFailCount, varFailCount + 1))` errors on the `Set(...)` line specifically. **Fix: `Collect()` into a fresh collection instead — `Collect` writes are still allowed inside `ForAll`, each iteration just appends its own row independently, so there's no ordering dependency.** Then set the variable ONCE, from `CountRows(collection)`, after the `ForAll` has finished (this is fine, it's no longer inside the loop):
  ```
  Clear(colFailures);
  ForAll(
      source As Row,
      IfError(
          Patch(...),
          Collect(colFailures, {ID: Row.ID})
      )
  );
  Set(varFailCount, CountRows(colFailures));
  ```
  Confirmed live: Policy Tracker `Main.yaml`'s `PublishAllBtn.OnSelect` — the original per-row `Set(varPublishFailCount, varPublishFailCount + 1)` inside the `ForAll`/`IfError` threw this exact error on paste into Studio. 2026-07-22.
  - **Follow-on error after applying the `Collect()` fix above: `IfError(Patch(...), Collect(...))` throws "Invalid argument type (Table). Expecting a Record value instead."** `Patch(...)` returns a **Record** (the patched row); `Collect(...)` returns the whole **Table** it just collected into. `IfError`'s two branches (the normal-path value and the error fallback) must resolve to the same type, and Record vs Table doesn't unify. **Fix: end both branches with `; true`**, so each side evaluates to a plain Boolean instead of its natural Patch/Collect return value — a statement chain's type is just whatever its last statement evaluates to, so appending `; true` sidesteps the mismatch entirely without changing behavior:
    ```
    IfError(
        Patch(...); true,
        Collect(colFailures, {ID: Row.ID}); true
    )
    ```
    Confirmed live: same `PublishAllBtn.OnSelect`, immediately after the `Set`-in-`ForAll` fix above, same session, 2026-07-22. **When pairing `IfError`'s success/fallback branches, always check that both sides' final function call returns the same type (or force it with `; true`) — don't assume any two working single-purpose calls (a `Patch` here, a `Collect`/`Remove`/etc. there) are automatically compatible as `IfError` siblings.**
- **`&` cannot concatenate two tables** — it's the text/scalar concatenation operator only (accepts Text/Number/Date/Time/DateTimeNoTimeZone/DateTime/Boolean/ViewValue/Dynamic — that exact list is what the error names). `Table({...}) & existingCollection` throws "Invalid argument type. Expecting one of the following: Text, Number, Date, ..." because Power Fx is trying to coerce both tables down to a scalar for `&`, not concatenating them as tables. **To prepend/append a record to an existing collection, don't try to build the combined table in one formula — mutate the collection itself**: `ClearCollect(coll, {newRow})` then `Collect(coll, ForAll(source, {...}))` (or just `Collect(coll, {newRow})` if not rebuilding from scratch). Confirmed on Policy Tracker `Main`, trying to inject a synthetic "All types" chip row ahead of `colChoiceColors` for a type-filter gallery. 2026-07-06.
- **In the browser editor's manual Insert-panel workflow (not YAML paste), `LayoutDirection` and `LayoutGap` are not settable via the property formula bar at all**, even though both are completely valid, working `GroupContainer@1.5.0` YAML properties (confirmed throughout this app's exported YAML — `DashboardContainer`, `ScreenContainer1`, etc. all use them). The Insert panel exposes three distinct container choices — **Container** (blank/ManualLayout), **Horizontal container**, **Vertical container** (both AutoLayout) — and picking one fixes the direction permanently at creation time; there is no property in the formula-bar "add property" list to change it afterward. Gap between children is set via a dedicated **Gap stepper control in the properties pane** (next to the alignment icons), not a formula. Every other AutoLayout property (`Fill`, `Height`, `Width`, padding, `DropShadow`) is a normal formula-bar property — only direction and gap are insert-time/UI-only. **When writing a guide for someone building manually in the browser (as opposed to a YAML paste), say "insert a Horizontal/Vertical container" and "set Gap via the properties pane stepper" — never "set LayoutDirection/LayoutGap via formula."** Confirmed by direct user correction on Policy Tracker `Main`, 2026-07-06.
- **The confirmed real YAML property name for "wrap children to a new row" on a `Variant: AutoLayout` `GroupContainer@1.5.0` is `LayoutWrap` (boolean, e.g. `LayoutWrap: =true`)** — this is settable via the formula bar, unlike `LayoutDirection`/`LayoutGap` above. Confirmed live in Policy Tracker `Main`'s `TypeFilterBar` export. Don't hedge this one as "check the properties pane" the way `LayoutDirection`/`Gap` need to be — `LayoutWrap` is a normal property.
- **Manual `X`/`Y` properties on a control whose immediate parent is `Variant: AutoLayout` are unreliable/likely ignored — AutoLayout positions children by flow order (plus padding/gap/alignment), not by coordinates.** Confirmed the hard way: a guide gave explicit `Y` values for `Title` inside `Container5` (an `AutoLayout` `LayoutDirection.Horizontal` container), and the user's real, working, live-tested build left `Title.Y` completely unchanged from its pre-AutoLayout value — changing it had no visible effect, exactly as it would if the property were being ignored. **Only give manual X/Y instructions for controls whose direct parent is `Variant: ManualLayout`.** For a control inside an `AutoLayout` parent, describe the change in terms of order/alignment/padding/gap instead, or explicitly flag "this parent is AutoLayout, X/Y may not apply — verify live." Policy Tracker `Main`, `ReleaseGallery` card thinning, 2026-07-07.
  - **A screen-level equivalent was suspected but NOT confirmed** in one build (see the `Scale to fit` entry below, which turned out to be the actual cause that time) — don't assume a screen has an "Auto layout" toggle without seeing it live. Some Studio versions/app types have no such screen-level setting at all; the Properties panel for a screen may only expose Fill/BackgroundImage/LoadingSpinner/Height with nothing resembling a layout-mode toggle. If a screen's controls all bunch together regardless of their authored `Y` values AND regardless of `Screen.Height`, check `Scale to fit` (App settings → Display) before concluding it's an AutoLayout screen.
- **App setting `Scale to fit` (App settings → Display, ON by default in most templates) proportionally shrinks an entire screen's rendered content to match the actual device viewport, regardless of that screen's own `Height`/`Width` property.** This means increasing a screen's `Height` to make it scroll does **nothing** if `Scale to fit` is on — the runtime just scales the whole layout down harder to keep cramming it into the same fixed frame, so controls with very different authored `Y` values (e.g. Y=750 vs Y=1470) end up rendered close together, visually indistinguishable from an overlap/AutoLayout bug. **Symptom: making a screen taller has zero effect on spacing — everything stays visually bunched at whatever size, "it doesn't matter how big I make it."** That's the tell it's `Scale to fit`, not AutoLayout and not a too-short-canvas problem. **Fix: App settings → Display → turn off `Scale to fit` (and `Lock aspect ratio` if present) — only then does increasing a screen's `Height` beyond the device viewport make it genuinely scroll** instead of scale. **Suspected (not yet live-confirmed)** in Branch Contact Groups `scrManageGroups` — the "Manage Members" panel (12+ controls, authored Y=750 to Y=1514) stayed bunched together after raising `Screen.Height` alone; ruled out a screen-level AutoLayout toggle first since the user's Properties panel had no such option. Update this entry to CONFIRMED once the user verifies `Scale to fit` was actually the fix. 2026-07-23.

## Power Automate

- **A "Power Apps (V2)" trigger input passed as Text (or any wrong type) when the downstream action's field expects a different type — the dynamic content picker on that field silently shows "No dynamic content available," with no error, indistinguishable from a genuinely broken/stuck picker.** Confirmed against Microsoft's own troubleshooting docs, not guessed: the dynamic content picker filters its suggestion list by matching the target field's data type — a Number-typed field (e.g. `Id` on any SharePoint `Update item`/`Get attachments`/`Delete attachment`/`Add attachment` action) will not list a Text-typed trigger input at all, even though the value genuinely exists and other Text-typed fields in the same action show it fine. Source: [Dynamic content picker missing dynamic content from previous steps](https://learn.microsoft.com/en-us/troubleshoot/power-platform/power-automate/flow-creation/dynamic-content-picker-missing-dynamic-content-from-previous-steps), "Cause 2: Data Type Mismatch". **Before building any flow that writes to a SharePoint list, check the real column type of every field you intend to target with a trigger input** — SharePoint's own numeric ID (used for every `Id` field) is always a Number, and any other column defined as a Number/Choice/Yes-No column in that specific list needs a matching-typed trigger input, not a default Text one. Going the other direction (Number value into a genuinely Text-typed SharePoint column) is fine and does not hit this filter — SharePoint's connector coerces it automatically. Confirmed live: Training Tracker `TT - Save Certificate` flow, 2026-07-30 — both `RecordID` (targets every `Id` field) and `CourseID` (targets `TT TrainingRecords.CourseID`, itself a Number column) were originally specified as Text, and both silently failed to appear as dynamic content on their Number-typed target fields until switched to Number inputs.
- **An unsaved Power Automate flow loses all trigger inputs on a browser refresh (F5).** There is no autosave/draft-restore for a flow that has never been saved once — if you hit a wall partway through building the trigger and refresh to try to unstick the editor, you lose every input added so far and must re-add them from scratch. If a flow won't save because a downstream required field has no value yet, do not refresh to try to fix it — fill the required field with any placeholder value first (see "SharePoint list with required columns forces every Update item to resupply them" below, if that's the actual cause), get the flow to save once, then go back and fix the value properly.
- **A SharePoint list with required columns forces every `Update item` action to resupply a value for those columns, even ones you have no intention of changing.** If a list's schema marks a column as required (e.g. `PersonEmail`, `CourseID`, `CourseName` on `TT TrainingRecords`), the `Update item` connector action shows a red asterisk and refuses to save the flow until every required column has something in it — not just the columns you actually want to update. **Fix: re-supply the existing/unchanged value from whatever the flow already has available (e.g. the same trigger input that carried it in), rather than leaving it blank or inventing a new value.** This does not change the stored data, it just satisfies the connector's validation. Confirmed live: Training Tracker `TT - Save Certificate` flow, 2026-07-30.
- **A `Respond to a PowerApp or flow` action cannot be nested inside an `Apply to each` (foreach) loop — Power Automate blocks this outright at save time with `InvalidWorkflowRunAction: ... type 'Response' ... could not be nested under an action of type 'foreach'`.** There is no setting to allow it; the Respond action is only valid as a top-level (or Condition-branch-level) action, never inside a loop. This also means `Initialize variable` actions have their own placement rule: they can only run at the very top level of the flow (directly after the trigger), never inside a Condition or loop either. **Fix for "fetch one file from inside a loop, then respond with it": initialize the needed variables (e.g. `varFileName`, `varFileContentBase64`) at the top level before any Condition; inside the loop, use `Set variable` to populate them (an action from inside a loop is otherwise an array once outside the loop, so a variable is the standard way to pull a single value back out); then place the actual `Respond to a PowerApp or flow` action after the loop ends, reading from the variables instead of from loop-scoped dynamic content.** Confirmed live: Training Tracker `TT - Get Certificate` flow, 2026-07-31 — the original guide draft placed Respond directly inside the `Apply to each` over `Get attachments`, which failed to save with exactly this error.
- **A flow cannot have two separate `Respond to a PowerApp or flow` actions at all — not even one per Condition branch with identically-named/typed outputs.** This is the deeper, easy-to-miss cause behind a specific, maddening symptom: the app's formula bar (e.g. `varResult.someField`) only ever autocompletes a subset of the fields actually configured on the Respond action(s) — commonly just the fields that happen to be identical across every branch — with no error, no warning, and the missing fields behaving as if they don't exist even though the flow designer visibly shows them configured correctly on both Respond actions. **This survives everything that looks like it should fix a caching problem** — removing/re-adding the flow as a data source (repeatedly), fully closing and reopening the app, correcting field name casing, even copying the entire flow to a brand-new name via "Save As" (the copy inherits the exact same broken behaviour, because the two-Respond-action structure itself is the defect, not any cached metadata). Confirmed via a moderator-verified answer on Microsoft's own Power Platform community forum: "You can't have two Respond to Power Apps actions in the same flow." **Fix: never use more than one `Respond to a PowerApp or flow` action in a flow, full stop.** Where a flow's logic branches (e.g. an authorisation Condition), set plain variables (e.g. `varSuccess` Boolean, `varMessage`/`varFileName`/`varFileContentBase64` String) inside each branch instead of responding from within it, then place exactly one Respond action after the branches rejoin, reading every output from those variables. Confirmed live: Training Tracker `TT - Get Certificate` flow, 2026-07-31 — two Respond actions (one per branch of the authorisation Condition), each configured with the same 4 outputs, still left the app only recognising `success`/`message`, across three separate remove/re-add cycles and one full flow duplication, until both Respond actions were removed and replaced with the single-Respond-plus-variables pattern.

- **A Power App does not automatically pick up changes made to a flow it already calls — editing and saving an action inside a flow (e.g. fixing a field name in a `Create item`/HTTP action's body) has no effect on an app already referencing that flow until the app's own connection to it is refreshed.** Confirmed live: a flow was edited and saved (verified via the flow's own run history showing the new action content correctly), yet a Power App calling that flow via `'FlowName'.Run(...)` kept executing the old, pre-edit logic on every subsequent test — the run history for the Power-App-triggered runs still showed the old field names, not the ones just saved in the designer. **Fix: in Power Apps Studio, open the Data panel, find the flow under Connections/Data sources, and refresh (remove-and-re-add, or the panel's own refresh action) that specific flow's reference — this is what forces the app to pick up the latest saved version.** Do this after *every* flow edit that a live app is already calling, not just the first time a flow is added as a data source. Confirmed live: Library &amp; File Plan Manager `Create Library (App)` flow, 2026-08-12 — a `Create_FilePlan_Row` body fix (renaming a broken SharePoint field) was correctly saved in the flow designer, confirmed via the flow's own Code view, yet a Power App test run still executed the pre-fix version until the app's flow connection was manually refreshed.

## Architecture

- Loading collections only in `App.OnStart` and not reloading in screen `OnVisible`. Collections go stale when data is added in a different screen or session.
- **Using `ClearCollect(colMemberships, 'BM Memberships')` as if it loads the whole SharePoint list.** It only caches the first client-side page, so later membership rows never reach the app. Confirmed 2026-07-27 on Branch Contact Groups: `scrSendEmail` showed `0 members` for groups such as CTC Frimley even though SharePoint `BM Memberships` had real rows. Counts and recipient generation were reading the incomplete local `colMemberships` collection. **Fix for Branch Contact Groups v2.32:** build `colGroups` first, then load memberships group-by-group: `ClearCollect(colMemberships, Filter('BM Memberships', false)); ForAll(colGroups As grp, Collect(colMemberships, Filter('BM Memberships', GroupName = grp.Title)))`. This avoids the global row cap because each query only fetches one group's roster.
- Not initialising `varViewingForSelf` at the top of `OnVisible`. If it is blank, a self-view/team-view screen will show nothing.
- Hard-coding `Set(varViewingForSelf, true)` in a gallery row `OnSelect`. This breaks team-member viewing.
- Not calling `ResetForm(frmCert)` in a form screen's `OnVisible`. The form shows stale data on revisit.
- Not resetting error border variables (`Set(varErrX, false)`) when opening an edit panel. Red borders from a previous failed save linger.
- Assuming a field exists in `varCourse` because it exists in SharePoint. The field only exists in `varCourse` if the `ForAll` projection that built `colMyTraining` included it.
- **A global collection populated with `AddColumns` (computing an extra field) in one place, and a plain `ClearCollect`/`Filter` (without that same `AddColumns`) in another.** Causes a visible flicker: whichever population runs last wins, and if the plain one runs after the computed one, every row loses the computed field and any UI reading it (e.g. an icon `Switch` on the computed column) falls back to its default/error branch — looking like the correct data displayed briefly then "broke". Confirmed: Policy Tracker `ViewItem_1` — the link gallery's file-type icons flickered from correct (PDF/Word/Excel) to a generic fallback icon on load, because the link-modal's "reload after edit" success handler computes `FileExt` via `AddColumns`, but the screen's own `OnVisible` repopulates the same collection with a plain `Filter` and no `AddColumns` at all. **Fix: every population point for a given collection must compute the same derived columns, not just the one call site that happened to need it first.** 2026-07-05.
  - **Recurred 2026-07-22 on the exact same collection (`colLinks`), a different set of call sites.** This time it wasn't a flicker-on-load, it was icons resetting to the generic fallback specifically on **Save/Save & Close of the parent item** (not the link modal) — the item's `btnSaveClose1`/`btnSave1` success handlers, plus the nested save-then-open-link-modal branch in `btnAddLinkEmpty1`, all did `ClearCollect(colLinks, Filter(PolicyLinks, PolicyTrackerSource.Id = varSavedRecord.ID))` with no `AddColumns`/`FileExt`, while `BuildLinksForSource` (the one that runs on opening the item) correctly computes it. Closing and reopening the item "fixed" it because that re-ran `BuildLinksForSource`. **Lesson confirmed twice now: whenever a collection's schema depends on a computed column, grep every `ClearCollect`/`Collect` call site for that collection name before trusting any single one of them is complete — this bug has now recurred by adding a brand-new population site (a Save handler) without checking the others, not by editing an existing one.**
- Using Badge control to render repeating pills. Badge cannot loop over a collection — use Gallery + Classic Button.

## YAML File Structure

- Any screen YAML that does not start with `Screens:\n  ScreenName:`. The pa-yaml-wrap tool and PowerApps Studio paste both require this exact top-level format. **Never use `- ScreenName:` (list item).** Always use the dict form: `Screens:` at col 0, screen name at col 2, `Properties:` and `Children:` at col 4.
- **Pasting a corrected screen YAML over an existing screen that already has that screen's name.** Studio's paste is additive/merge, not a full replace — controls present in the old paste but removed from the new YAML (e.g. a hidden staging control that got deleted as part of a fix) can be left behind on the canvas with their old, now-orphaned bindings still intact, still capable of throwing the old error. **When a fix removes or renames a control, tell the user to delete the whole screen and paste fresh into a new blank screen, not paste over the existing one.** Suspected cause of a fix "not taking" in Branch Contact Groups even after confirming the source YAML no longer contained the offending control. 2026-07-23.
- `GroupContainer@1.5.0` without a `Variant:` keyword. **Every GroupContainer requires `Variant: ManualLayout` (or `Variant: AutoLayout`) between `Control:` and `Properties:`.** Omitting it causes PA1011 on paste. Confirmed broken in Overview build.
- `LayoutMode: =LayoutMode.ManualLayout` inside a GroupContainer's Properties block. **Do not set LayoutMode as a property — `Variant: ManualLayout` already implies it.** Including it causes PA2108. Remove the property entirely.
- `Gallery@2.15.0` without a `Variant:` keyword. **Every Gallery requires `Variant:` between `Control:` and `Properties:`.** Use `Variant: Vertical` for vertical galleries, `Variant: Horizontal` for horizontal galleries, `Variant: VariableHeight` for variable-height galleries. Confirmed PA1011 in Overview build.
- `Layout: =Layout.Horizontal` or `Layout: =Layout.Vertical` inside a Gallery's Properties block. **Do not set Layout as a property — the Variant already implies it.** PA2108 if present. Same rule as LayoutMode on GroupContainers. Confirmed in Overview build.
- `Image@2.2.0` — stale. **Always use `Image@2.2.3`.** PA2105 warns it may produce errors.
- **`SelectedDate` as a settable property on `ModernDatePicker@x.x.x` — do not use, in any version.** Confirmed broken on both `1.0.0` and `1.0.1` in this environment: `PA2108: Unknown property 'SelectedDate'`, identical error, same lines, after bumping the declared version (see the now-corrected theory below). Microsoft's own docs page lists `SelectedDate` as a real property of the control family, and it genuinely works as a *read* (`Self.SelectedDate` inside the control's own `OnChange`, or `otherControl.SelectedDate` from elsewhere) — but it is **not a valid key to author in the YAML `Properties:` block** for this control, at least not in the version(s) tested. Do not attempt a version bump as the fix (already disproven — see below). **Use `DatePicker@0.0.46` instead** (a different, non-"Modern"-prefixed control family) — confirmed live and working in Policy Tracker's real `ViewItem.yaml`/`ViewItem_1.yaml`, which sets `SelectedDate` successfully on that control tag with a full property set (`BorderColor`, `BorderStyle`, `BorderThickness`, `Fill`, `Font`, `FontSize`, `Format`, `Height`, `IsEditable`, `OnChange`, `SelectedDate`, `StartOfWeek`, `Width`). It is not Classic-prefixed and renders with the same rounded/modern look as the rest of the app, so it satisfies "no Classic-style sharp corners" without the `ModernDatePicker` schema problems.
  - **Corrected theory (originally recorded here, then disproven):** the first hypothesis was that `ModernDatePicker@1.0.0`'s declared version was simply stale — the same paste's `PA2105` warning named `1.0.1` as current, so the version was bumped to `1.0.1` expecting that to fix it. It did not; the identical `PA2108` error recurred against `1.0.1`. **Lesson: a `PA2105` version-mismatch warning appearing alongside a `PA2108` unknown-property error does not mean the version bump will fix the error** — they can be two independent issues. Don't assume a version bump resolves a property error without re-testing; if it doesn't, stop iterating on that control and switch to a proven alternative rather than guessing a third property name or version. Policy Tracker `ViewItem_1`, 2026-07-05.
  - **Recurred 2026-07-08, via a different path — manually rebuilding the field in the browser editor's Insert panel, not a YAML paste.** `dpkPublishDate1` on the real `ViewItem` ended up back on `ModernDatePicker@1.0.1` (someone picked "Modern Date picker" from Insert instead of the plain "Date picker"), with `DefaultDate` instead of `SelectedDate` and `FontWeight: =""`/`Placeholder` (neither a real property on the Classic control). Symptom: `Format: =Text(Self.SelectedDate, "d mmmm yyyy")` rendered as a squashed "2 Jul26 2026" instead of "2 July 2026".
  - **Actual root cause, found by the user directly, not by guesswork — `ModernDatePicker.Format` does not support the 4-`m` `"mmmm"` token (full month name) the way `DatePicker@0.0.46.Format` does.** With four `m`s, the control silently mis-renders instead of erroring — no PA error, no warning, just garbled output ("2 Jul26 2026"). **Fix confirmed live: drop to three `m`s, `"d mmm yyyy"`, and the exact same `ModernDatePicker@1.0.1` control renders correctly ("2 Jul 2026")** — no control swap needed at all. **This means the SelectedDate-authoring bug earlier in this entry and the Format 4-`m` bug are two separate, independent problems with this control, not the same root cause** — don't assume the whole control is unusable just because one specific property has a known issue; test the actual failing property in isolation (here, just the format string) before reaching for a full control replacement. Getting this wrong meant recommending a disruptive full-control swap (with a guide page and a GitHub issue reply) for a one-character fix. Policy Tracker `ViewItem`, 2026-07-08.
  - `DatePicker@0.0.46`'s two syntax quirks worth remembering if a control swap is ever genuinely needed for a different reason: `SelectedDate` (not `DefaultDate`) is the settable property, and `StartOfWeek` takes a **plain quoted string** (`="Monday"`), not the `StartOfWeek.Monday` enum `ModernDatePicker` uses.
  - **`Tooltip` on `DatePicker@0.0.46` is not a valid YAML property.** Confirmed by user paste error on Training Tracker `scrCourseDetail` `DataCardValue1`, 2026-07-27: `PA2108: Unknown property 'Tooltip' for control type 'DatePicker@0.0.46'`. Remove the property; use a nearby label/help text if the date field needs an explanation.
  - **There is a THIRD date picker control, `Classic/DatePicker@2.6.0`, with the OPPOSITE convention from `DatePicker@0.0.46` right above it** — easy to confuse since both are non-"Modern"-prefixed and both look like the obvious "just use the classic one" choice. On `Classic/DatePicker@2.6.0`, **`DefaultDate` is the settable property, and `SelectedDate` is read-only** (used elsewhere, e.g. `otherControl.SelectedDate`, exactly like `ModernDatePicker` — not like `DatePicker@0.0.46` just above). Setting `SelectedDate` directly in this control's own `Properties:` block throws `PA2108: Unknown property 'SelectedDate'`, confirmed via the real Layer A parser (not guessed) 2026-07-24, Branch Contact Groups `scrManageGroups` `datEditManualDate`. **This is exactly the trap the top of this file warns about** — assumed `SelectedDate` carried over from the `DatePicker@0.0.46` entry two lines up because both are "Classic-ish," shipped it without testing, and it was real, live, reported via GitHub issue. When in doubt between these two, write a tiny isolated test screen and run it through `payaml-validate`'s Layer A directly — that's what actually resolved this, not re-reading docs or guessing a third property name.

- **`Attachments@2.3.0` placed directly on a screen, unbound, expecting it to work as a standalone file picker.** Microsoft's own control docs state a real limitation: "The upload and delete functionality works only inside a form. The attachment control appears disabled when in Edit mode and not inside a form." A bare `Attachments@2.3.0` control with no `Form@2.4.4` parent — the obvious first attempt for "let the user pick a file without writing to a live SharePoint list" — renders but silently refuses add/delete clicks. **Confirmed working alternative, already live-audited in this repo's own `reference/external/powerappsui/file-upload.md` component:** set `Items: =Blank()` (not omitted, not a schema-less `[]` literal — `Blank()` specifically), give the control `OnAddFile` that does `Clear(colStaging); Collect(colStaging, Last(Self.Attachments)); Reset(Self)` to capture the picked file into a local collection and immediately empty the control's own internal state, and place a fully transparent click-catcher Button *behind* the Attachments control (declared earlier in `Children`, per normal z-order rules) with `OnSelect: =Select(attachmentsControlName)` to trigger the native picker from custom-styled "click to select / drag and drop" UI. With `Items: =Blank()`, the control is not bound to any real data source, so the "must be inside a form" limitation — which is about persisting SharePoint/Dataverse attachment changes via `SubmitForm` — does not apply; the control still fires `OnAddFile` and exposes `Self.Attachments` (`Name`/`Value` fields, `Value` being the file blob) normally. Read the staged file back out of the local collection (`First(colStaging).Name` / `.Value`), e.g. to pass into a `Flow.Run(...)` call, never from the control's own `.Attachments` after the fact (it gets `Reset()` on every add). Confirmed live: Training Tracker `scrCourseDetail` certificate-upload redesign, 2026-07-30 — first draft used a bare unbound `Attachments@2.3.0` with no click-catcher, caught against the MS docs mirror before shipping (not live-tested broken), then rebuilt using the proven `powerappsui` pattern.

- **A data-source action function (e.g. `Office365Users.SearchUserV2(...)`) written directly inside a Gallery's `Items` property.** Unlike a pure formula, an action call sitting in a reactive property gets re-evaluated far more often than "once per keystroke" — any unrelated re-render elsewhere on the screen can re-trigger it, since Power Apps re-runs the whole dependency graph, not just the control that changed. Symptom reported live: a thin loading indicator repeatedly sweeping across the top of the screen ("like PowerApps is constantly trying to update something"), and an open lightbox that felt unstable while search boxes with this pattern were present nearby. **Fix: never put a connector/action call directly in `Items`. Move it to the search textbox's `OnChange`, doing an explicit `ClearCollect(colName, Office365Users.SearchUserV2(...))`, and point the gallery's `Items` at that plain collection instead.** Same live-as-you-type UX (OnChange still fires on every keystroke), but the connector is only invoked when the text actually changes, not on every unrelated re-render. Remember `Reset()` does not fire `OnChange` — anywhere the search textbox gets `Reset()`'d (closing a lightbox, after a successful add), also explicitly re-`ClearCollect` the results back to empty, or the stale results silently linger. Confirmed pattern in Branch Contact Groups `scrManagePeople` (`galAddPersonGalResults`) and `scrManageGroups` (`galGalResults`, `galMaintainerGalResults`), 2026-07-29 — all three had the connector call directly in `Items`.

## Unverified — Found in Microsoft Docs, Not Yet Empirically Confirmed In This App

Per user directive 2026-07-06 ("don't just search Microsoft documents, Bible is the Bible... if it's not in the Bible, put it in there"): Microsoft Learn is no longer a default first-resort source for this app family — it has already been the direct source of confirmed-wrong claims here (`ModernDatePicker.SelectedDate`, `ModernCombobox` instability). Anything below was found via Learn/WebSearch, not by pasting it into the real editor, and must be moved out of this section into the main body (with the app/screen/date it was confirmed on) the first time it's actually verified working live — or deleted if it turns out wrong.

- **Horizontal container "Horizontal Overflow" property — likely YAML name `LayoutOverflowX`, value `LayoutOverflow.Scroll` / `LayoutOverflow.Hide`.** Per Microsoft's Horizontal container control doc page: "Horizontal Overflow - Defines whether the container shows scrollbars or remove content when it is too large to fit. Scroll and Hide." This is presented as a properties-pane UI setting (like Gap/Direction), not necessarily a formula-bar property — same insert-time/UI-only pattern already confirmed for `LayoutDirection`/`LayoutGap` on this control family. Parallels the already-confirmed `LayoutOverflowY` (vertical AutoLayout scroll, in the main Control Properties section above) closely enough to be plausible, but not tested live here. **Before relying on this: check the properties pane for an "Overflow" setting near Gap/Direction on the Horizontal container in question.**

## Guide-Writing

- “Same as above.”
- “Duplicate the previous screen.”
- “Use the same technique.”
- Long inline control paragraphs that hide exact properties.
- Background/card/panel/gallery-row controls without a layer-order note when they can cover other controls.
- Formulas that may temporarily red-line without a reassurance note explaining what later control/collection/variable resolves it.
- Grouping instructions that hide layer-order requirements or fail to list exactly which controls are included/excluded.
- Long property lists that mix actual changes with default values without marking which are defaults.
- Repeating fresh-control defaults from `powerapps-control-defaults.md` as if the user must manually change them.
- Repeating `Fill=RGBA(0,0,0,0)` on normal Label controls as if it must be manually changed.
- Repeating `BorderThickness=0` on normal Label controls as if it must be manually changed.
- Repeating `SelectMultiple=true` on modern Combo box controls as if it must be manually changed.
- Repeating `ItemDisplayText=ThisItem.Value` on modern Combo box simple value tables as if it must be manually changed.
- Treating a property omitted from the fresh-control export as a guaranteed literal default value.
- Control rows that say only “see formula below” when the formula can be placed in a collapsed copyable block beside the control.
- Any instruction that depends on visual guessing rather than exact control names and properties.
- Re-pasting full screen YAML to fix a single property. Targeted property changes are always safer and preserve existing customisations.
