Skip to the content.

Rule reference

Every rule has a stable id (PAxxxx / PFxxxx), a name, a category and a default severity. Configure any rule by id or name in .easel.yml (see configuration). Disable with off, or change severity.

Severities: error, warning, info.


Performance

PA1001 — non-delegable-query · warning

A Filter / LookUp / Search over a data source (not an in-memory collection or variable) that contains a non-delegable function. Non-delegable operations run only over the first 500–2000 rows. Conservative: fires only when the query root is a bare data source and a known non-delegable function appears inside the query.

PA1002 — n-plus-one · warning

A ForAll(...) whose body performs a per-row data operation (Patch/Collect/Remove/LookUp/Update). Batch instead: build a table and Patch/Collect once.

PA1005 — screen-control-limit · warning

A screen with more controls than the limit (max, default 300). Split into screens or components. { max: <int> }.

PA1006 — heavy-onstart · warning

App.OnStart with many assignments or a large AST. Move derived values to named formulas (App.Formulas) so they evaluate lazily. { max-assignments: 6, max-nodes: 80 }.

PA1014 — nested-forall · warning

A ForAll nested inside another ForAll — O(n²) row iteration. Precompute the inner lookup once (With / AddColumns / GroupBy) instead of re-iterating per row.

PA1015 — repeated-expensive-call · warning

The same expensive call (LookUp/Filter/Sort/Sum/…) repeated verbatim within one formula — each occurrence re-evaluates the query. Wrap it in With({result: <call>}, …) and reuse. { min-length: 20, min-occurrences: 2 }.

PA1016 — countrows-filter · info

CountRows(Filter(source, condition)) materialises the filtered table just to count it. Use CountIf(source, condition) — it counts without materialising and delegates on more sources.

PA1017 — first-filter · info

First(Filter(source, condition)) fetches a whole filtered table to use one row. Use LookUp(source, condition) (with an optional third argument for the field).

PA1018 — sequential-data-loads · info

Two or more Collect/ClearCollect calls running sequentially in OnStart/OnVisible outside a Concurrent(...). Independent loads in Concurrent run in parallel and cut startup time. { min-loads: 2 }.

PA1019 — inefficient-delayed-load · warning

A formula references a control that lives on another screen, forcing that screen to load eagerly (App checker: Inefficient delay loading). Share the value via a variable, collection or Navigate context record instead. Conservative: fires only for control names with exactly one definition in the app, so shadowed or reused names never produce a false positive.

PA1028 — delay-output-text-input · info

A Text input whose .Text feeds a Filter/LookUp/Search without DelayOutput enabled — the query re-runs on every keystroke (solution checker: app-use-delayoutput-text-input). Set DelayOutput to true.

PA1029 — large-media · info

An embedded media asset larger than the limit (max-kb, default 300). Large media slows app download and load — compress, lower resolution, or prefer SVG. { max-kb: <int> }.


Maintainability

PA1003 — unused-variable · warning

A variable (Set/UpdateContext) or collection (Collect/ClearCollect) that is assigned but never read anywhere in the app.

PA1004 — unused-media · info

A media asset that is not referenced by identifier or by name in any formula. Name matching is whole-token: logo counts as referenced in "https://x/logo.png" but not in "logotype", so a substring can never mask a genuinely unused asset.

PA1008 — hardcoded-color · info

A colour property (Fill, Color, BorderColor, …) using an RGBA(...) / ColorValue("#...") / Color.* literal instead of a theme/global value.

PA1010 — deep-nested-if · info

If nested deeper than threshold (default 2). Flatten with Switch(...) or intermediate variables. { threshold: <int> }.

PA1011 — timer-side-effect · info

A Timer whose OnTimerEnd/OnTimerStart performs writes or navigation. Hidden periodic side-effects are hard to reason about.

PA1012 — duplicate-formula · info

The same non-trivial formula repeated across the app. Extract into a named formula or a component. { min-length: 40, min-occurrences: 3 }.

PA1013 — inconsistent-control-version · info

The same control type used at different @versions across the app.

PA1031 — unused-datasource · info

A data source the app binds to but never references. Every bound source costs connection setup at app start.

PA1027 — unused-screen · info

A screen that nothing references — no Navigate, no StartScreen, no formula. Fires only when App.StartScreen is set: with a known start screen a zero-reference screen is provably unreachable; without it the start screen cannot be determined from source, so the rule stays silent rather than guess.


Accessibility

Aligned with the Power Apps Studio Accessibility checker — each rule maps to one of its issue types.

PA1009 — missing-accessible-label · warning

An interactive control (button, input, toggle, …, or anything with OnSelect) with no non-empty AccessibleLabel.

PA1020 — focus-not-visible · warning

A control with FocusedBorderThickness set to 0 — keyboard users cannot see where focus is. (Focus isn’t showing.)

PA1021 — missing-captions · warning

An Audio/Video control with no ClosedCaptionsUrl. (Missing captions.)

PA1022 — default-screen-name · info

A screen still named Screen1, Screen2, … — screen readers announce screen names on navigation. (Revise screen name.)

PA1023 — positive-tab-index · info

A control with TabIndex greater than 0. Custom tab orders are hard to maintain and break screen readers — use 0/-1 and reorder with layout. (Check the order of the screen items.)

PA1024 — autostart-media · warning

An Audio/Video control with AutoStart true. Autoplaying media is disorienting and hard to stop for keyboard users. (Solution checker: app-avoid-autostart.)

PA1025 — state-indication · info

A stateful control (toggle, slider, rating, checkbox) with ShowValue false — users get no confirmation of its state. (Add State indication text.)

PA1026 — pen-alternative-input · info

A Pen input on a screen with no Text input — some users cannot use a pen and need another way to provide the information. (Add another input method.)

PA1030 — low-contrast · info

Text colour vs fill contrast below WCAG AA (min-ratio, default 4.5:1). Checked only when both Color and Fill are opaque RGBA(...) literals on the same control — easel never guesses inherited or computed colours. { min-ratio: <number> }.


Naming

PA1007 — naming-convention · warning (opt-in)

Element names must match configured regexes. Silent unless patterns are configured, so it never produces false positives out of the box.

naming-convention:
  patterns:
    variable: "^(gbl|var|loc)[A-Z]"
    collection: "^col[A-Z]"
    screen: "^scr[A-Z]"
    control: "^(btn|lbl|txt|gal)[A-Z]"

Security

Shared detectors with the secrets command (regex + Shannon entropy). Allowlist known-safe values under the rule’s allowlist.

PA2001 — hardcoded-secret · error

A literal that looks like an API key, access token or connection string with a secret.

PA2002 — high-entropy-literal · warning

A long, high-entropy literal that may be a generic secret.

PA2003 — url-with-credentials · error

A URL of the form https://user:pass@host.


Errors

PA0002 — file-load-error · error

A whole source file failed to parse (malformed YAML). Reported with its location so a broken file is never silently dropped from analysis — fix the YAML, or re-open and re-save in Power Apps Studio.

PF0001 — unparsable-formula · error

A formula Power Fx could not parse. Fix the syntax, or (for legacy apps) re-open and re-save in Power Apps Studio to export the current pa.yaml format. easel never crashes on an unparsable formula — it reports it and continues.

PF0002 — unknown-navigation-target · error

Navigate(...) or SetFocus(...) whose first argument is a name defined nowhere in the app. The App checker reports this class of issue as a formula error; easel checks the navigation functions statically, without needing live binding.

PF0003 — possible-typo · warning

An identifier that resolves to nothing in the app but is one edit away from a defined symbol (txtSerchtxtSearch). Names a formula introduces itself (With fields, As aliases), record-scope fields and built-in identifiers are exempt, so gallery and filter field names never false-positive.

PF0004 — wrong-argument-count · error

A built-in function called with an argument count outside its documented signature (Left("abc") — needs 2). Bounds come from a curated catalog of ~150 functions; functions outside the catalog are never flagged.

PF0005 — unknown-function · warning

A call to a function that is neither a known built-in nor defined in the app (named formula / user-defined function). Namespaced calls (Calendar.MonthsLong(), component functions) are skipped. Intentional names go under the rule’s allow list:

unknown-function:
  allow: [MyCustomConnectorAction]