---
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
