- Replace GetSitePropertiesFromSharePoint("", true) with modern
GetSitePropertiesFromSharePointByFilters using null StartIndex
- Use ctx.Clone(adminUrl) instead of creating new AuthenticationManager
for admin URL, eliminating second browser auth prompt
Resolves: UAT issue "Must specify valid information for parsing in the string"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
90 lines
4.8 KiB
Markdown
90 lines
4.8 KiB
Markdown
---
|
|
status: awaiting_human_verify
|
|
trigger: "SitePickerDialog shows 'Must specify valid information for parsing in the string' error when trying to load sites after a successful tenant connection."
|
|
created: 2026-04-07T00:00:00Z
|
|
updated: 2026-04-07T00:00:00Z
|
|
---
|
|
|
|
## Current Focus
|
|
|
|
hypothesis: ROOT CAUSE CONFIRMED — two bugs in SiteListService.GetSitesAsync
|
|
test: code reading confirmed via PnP source
|
|
expecting: fixing both issues will resolve the error
|
|
next_action: apply fix to SiteListService.cs
|
|
|
|
## Symptoms
|
|
|
|
expected: After connecting to a SharePoint tenant (https://contoso.sharepoint.com format), clicking "Select Sites" opens SitePickerDialog and loads the list of tenant sites.
|
|
actual: SitePickerDialog opens but shows error "Must specify valid information for parsing in the string" instead of loading sites.
|
|
errors: "Must specify valid information for parsing in the string" — this is an ArgumentException thrown by CSOM when it tries to parse an empty string as a site URL cursor
|
|
reproduction: 1) Launch app 2) Add profile with valid tenant URL 3) Connect 4) Authenticate 5) Click Select Sites 6) Error appears in StatusText
|
|
started: First time testing this flow after Phase 6 wiring was added.
|
|
|
|
## Eliminated
|
|
|
|
- hypothesis: Error comes from PnP's AuthenticationManager.GetContextAsync URI parsing
|
|
evidence: GetContextAsync line 1090 does new Uri(siteUrl) which is valid for "https://contoso-admin.sharepoint.com"
|
|
timestamp: 2026-04-07
|
|
|
|
- hypothesis: Error from MSAL constructing auth URL with empty component
|
|
evidence: MSAL uses organizations authority or tenant-specific, both valid; no empty strings involved
|
|
timestamp: 2026-04-07
|
|
|
|
- hypothesis: UriFormatException from new Uri("") in our own code
|
|
evidence: No Uri.Parse or new Uri() calls in SiteListService or SessionManager
|
|
timestamp: 2026-04-07
|
|
|
|
## Evidence
|
|
|
|
- timestamp: 2026-04-07
|
|
checked: PnP Framework 1.18.0 GetContextAsync source (line 1090)
|
|
found: Calls new Uri(siteUrl) — valid for admin URL
|
|
implication: Error not from GetContextAsync itself
|
|
|
|
- timestamp: 2026-04-07
|
|
checked: PnP TenantExtensions.GetSiteCollections source
|
|
found: Uses GetSitePropertiesFromSharePointByFilters with StartIndex = null (for first page); OLD commented-out approach used GetSitePropertiesFromSharePoint(null, includeDetail) — note: null, not ""
|
|
implication: SiteListService passes "" which is wrong — should be null for first page
|
|
|
|
- timestamp: 2026-04-07
|
|
checked: Error message "Must specify valid information for parsing in the string"
|
|
found: This is ArgumentException thrown by Enum.Parse or string cursor parsing when given "" (empty string); CSOM's GetSitePropertiesFromSharePoint internally parses the startIndex string as a URL/cursor; passing "" triggers parse failure
|
|
implication: Direct cause of exception confirmed
|
|
|
|
- timestamp: 2026-04-07
|
|
checked: How PnP creates admin context from regular context
|
|
found: PnP uses clientContext.Clone(adminSiteUrl) — clones existing authenticated context to admin URL without triggering new auth flow
|
|
implication: SiteListService creates a SECOND AuthenticationManager and triggers second interactive login unnecessarily; should use Clone instead
|
|
|
|
## Resolution
|
|
|
|
root_cause: |
|
|
SiteListService.GetSitesAsync has two bugs:
|
|
|
|
BUG 1 (direct cause of error): Line 50 calls tenant.GetSitePropertiesFromSharePoint("", true)
|
|
with empty string "". CSOM expects null for the first page (no previous cursor), not "".
|
|
Passing "" causes CSOM to attempt parsing it as a URL cursor, throwing
|
|
ArgumentException: "Must specify valid information for parsing in the string."
|
|
|
|
BUG 2 (design problem): GetSitesAsync creates a separate TenantProfile for the admin URL
|
|
and calls SessionManager.GetOrCreateContextAsync(adminProfile) which creates a NEW
|
|
AuthenticationManager with interactive login. This triggers a SECOND browser auth flow
|
|
just to access the admin URL. The correct approach is to clone the existing authenticated
|
|
context to the admin URL using clientContext.Clone(adminUrl), which reuses the same tokens.
|
|
|
|
fix: |
|
|
1. Replace GetOrCreateContextAsync(adminProfile) with GetOrCreateContextAsync(profile) to
|
|
get the regular context, then clone it to the admin URL.
|
|
2. Replace GetSitePropertiesFromSharePointByFilters with proper pagination (StartIndex=null).
|
|
|
|
The admin URL context is obtained via: adminCtx = ctx.Clone(adminUrl)
|
|
The site listing uses: GetSitePropertiesFromSharePointByFilters with proper filter object.
|
|
|
|
verification: |
|
|
Build succeeds (0 errors). 144 tests pass, 0 failures.
|
|
Fix addresses both root causes:
|
|
1. No longer calls GetOrCreateContextAsync with admin profile — uses Clone() instead
|
|
2. Uses GetSitePropertiesFromSharePointByFilters (modern API) instead of GetSitePropertiesFromSharePoint("")
|
|
files_changed:
|
|
- SharepointToolbox/Services/SiteListService.cs
|