Skip to content

fix: normalize site URLs + fail-fast on 404 NotFound (Stellantis batch) - #51

Merged
luigilink merged 3 commits into
mainfrom
fix/url-normalization-notfound-failfast
Sep 29, 2026
Merged

luigilink merged 3 commits into
mainfrom
fix/url-normalization-notfound-failfast

Conversation

@luigilink

Copy link
Copy Markdown
Owner

Summary

Reported by Stellantis (Denis) on a 3957-site batch. Two stacked problems:

  1. Polluted URLs. The site list was built by copy-pasting from a browser/OneDrive, so URLs
    carried a sharing-link query string (?xsdata=...&sdata=...&ovuser=...). Passing such a URL to
    Get-PnPSiteVersionPolicy returns 404 NotFound.
  2. Retry storm on 404. A 404 is neither auth, access-denied nor throttling, so it fell into the
    generic backoff and was retried 5× (10+20+40+80+160 s ≈ 5 min per site) — crippling at
    tenant scale.

Changes

  • Get-NormalizedSiteUrls — strips query strings/fragments, trailing slashes and whitespace,
    and de-duplicates. Applied to the explicit SiteUrls (before the sign-in anchor is chosen) and,
    defensively, to the SiteScope: All enumeration.
  • Test-IsNotFoundError + fail-fast in Invoke-RetryCommand — a missing site cannot be
    conjured by retrying, so 404 fails fast instead of burning the backoff budget.
  • New per-site NotFound outcome — skipped (not failed), with report badge / KPI card,
    end-of-run count and an advisory pointing at the canonical URL shape. Re-thrown from the drift
    read and the Legacy / apply / batch-delete inner catches so it reaches the single per-site handler.
  • Docs: CHANGELOG / RELEASE-NOTES under [Unreleased]; wiki resilience section, outcome list and a
    NotFound troubleshooting entry.

Testing

  • Pester: 165 pass (+5: NotFound detection, retry fail-fast on 404, URL normalization, plus
    structural guards).
  • Helper verified functionally: 7 mixed URLs (polluted / trailing slash / whitespace / duplicate /
    empty) → 4 clean canonical URLs.

Notes

  • No version bump (stays under [Unreleased], per RELEASING.md). A 3.2.1 patch release will be
    cut after merge.
  • Separately noted in the backlog: an opt-in JIT site collection admin (grant → run → revoke)
    workflow requested by Denis, to be designed as its own security-sensitive chantier.

Reported by Stellantis (Denis): site lists built by copy-pasting from a browser/OneDrive carry
sharing-link query strings (?xsdata=...&sdata=...&ovuser=...). Passing such a URL to
Get-PnPSiteVersionPolicy returns 404 NotFound, and the generic retry then retried it 5x with
exponential backoff (~5 min per site) — crippling on a 3957-site batch.

- Add Get-NormalizedSiteUrls: strip query strings/fragments, trailing slashes and whitespace,
  and de-duplicate. Applied to explicit SiteUrls before the sign-in anchor is chosen, and
  defensively to the SiteScope:All enumeration.
- Add Test-IsNotFoundError and fail fast on 404 in Invoke-RetryCommand (structural error — a
  missing site cannot be conjured by retrying). Re-throw NotFound from the drift read and the
  Legacy/apply/batch-delete inner catches so it reaches the single per-site handler.
- New per-site NotFound outcome: skipped (not failed), with report badge/KPI card, end-of-run
  count and advisory pointing at the canonical URL shape.

Docs updated (CHANGELOG/RELEASE-NOTES under [Unreleased]; wiki resilience, outcome list and a
NotFound troubleshooting entry). Tests: 165 pass.

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.

Copilot review overview

🟡 Changes recommended

Not-found classification can mislabel non-site failures, and normalization can silently leave no sites to process.

Review effort: Balanced
Findings: 3 Medium severity

Open (3)
What changed in this PR

Normalizes SharePoint site URLs and avoids retry storms for missing sites.

Changes:

  • Adds URL normalization and de-duplication.
  • Introduces fail-fast 404 handling and NotFound reporting.
  • Adds tests and documentation for the new behavior.
File Description
scripts/​SPSCleanVersions.ps1 Implements normalization and NotFound handling.
tests/​SPSCleanVersions.Tests.ps1 Tests normalization, classification, and reporting.
wiki/​Usage.md Documents missing-site behavior.
wiki/​Home.md Updates resilience overview.
wiki/​Configuration.md Adds configuration and troubleshooting guidance.
RELEASE-NOTES.md Records unreleased changes.
CHANGELOG.md Documents user-visible behavior.

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

Comment thread scripts/SPSCleanVersions.ps1 Outdated
Comment thread scripts/SPSCleanVersions.ps1
Comment thread scripts/SPSCleanVersions.ps1 Outdated
…ation, library-level NotFound

- Test-IsNotFoundError: drop the bare 'not found' alternative — it mis-classified unrelated
  failures (missing list/column/certificate) as a missing site. Only explicit HTTP-status
  evidence counts now ('status code is NotFound' / 404); '(404) Not Found' stays covered.
- Selected scope: fail configuration validation when normalization leaves no usable URL
  (e.g. SiteUrls = ['', '  ']), instead of silently reporting a successful zero-site run.
- Legacy Set-PnPList catch: a NotFound identifies the targeted library (e.g. deleted between
  enumeration and update), not the site — record it as a library-level Failed and continue,
  rather than re-throwing and marking the whole site NotFound. Access-denied still bubbles up
  (it implies no rights on the whole site). Site-level NotFound is still caught by Get-PnPList.

Tests: 167 pass.

@luigilink luigilink left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

LGTM

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.

Copilot review overview

🟢 Approval recommended

The implementation is coherent and tested; only a minor documentation clarification remains.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
Resolved since last review (3)

Comment thread wiki/Configuration.md Outdated
…r-URL

Copilot review: the previous wording implied a list composed entirely of sharing-link URLs
could still 404, but normalization strips each URL's query string independently, so list
composition is irrelevant. The real residual case is a copied link whose PATH is not a
site-collection URL (e.g. /:f:/s/..., /personal/..., or a deep document path) — describe that
instead.

@luigilink luigilink left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

LTGM

@luigilink luigilink self-assigned this Sep 29, 2026
@luigilink
luigilink merged commit 383dc76 into main Sep 29, 2026
1 check passed
@luigilink luigilink mentioned this pull request Sep 29, 2026
@luigilink
luigilink deleted the fix/url-normalization-notfound-failfast branch September 29, 2026 09:12
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.

2 participants