Fix PathNotFound error in msexports ETL ingestion on first run - #2247
Fix PathNotFound error in msexports ETL ingestion on first run#2247Michael Flanakin (flanakin) wants to merge 2 commits into
Conversation
The "Get Existing Parquet Files" GetMetadata activity in both msexports_ETL_ingestion (Microsoft.CostManagement/Exports/app.bicep) and the analytics ingestion ETL pipeline (Microsoft.FinOpsHubs/Analytics/app.bicep) failed with PathNotFound when the destination folder had never been created, e.g. the first ingestion for a scope/dataset/month combination. Recommendation exports hit this most often because their path includes an extra exportName segment, but any export type can hit it on first run. Add 'exists' to the GetMetadata fieldList, which is ADF's documented way to make the activity return exists:false instead of failing when the path is missing. This mirrors the pattern already used by other GetMetadata activities in this codebase (Check Schema in Exports/app.bicep, and the two GetMetadata activities in IngestionQueries/app.bicep). Update the downstream Filter activity's items expression to check .output.exists before reading childItems, so a missing folder now resolves to an empty file list instead of failing the pipeline. Fixes #2088
Change the 'Filter Out Current Exports' activity's dependency on 'Get Existing Parquet Files' from 'Completed' to 'Succeeded' in Exports/app.bicep. ADF's 'Completed' condition means succeeded OR failed. Now that 'exists' is in the fieldList (#2088 fix), a missing folder resolves as a clean 'Succeeded' result, so 'Completed' no longer serves a purpose for that case. Its only remaining effect was to let genuine failures (permissions errors, wrong storage account, throttling) silently flow through as if there were no existing files, which would prevent superseded parquet files from being deleted and cause duplicated data in reports with a green pipeline run. This matches the Analytics/app.bicep version of the same downstream filter activity, and the precedent in IngestionQueries/app.bicep, both of which already use 'Succeeded'. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Roland Krummenacher (RolandKrummenacher)
left a comment
There was a problem hiding this comment.
Re-reviewed at 73a0076. Both of my threads are settled — the dependency-condition change is a better fix than what I suggested, and the Analytics hunk holds up once you account for how the pipeline is triggered. Details in the threads.
Net change against dev is +5/-3 across two files, and the three Get Existing Parquet Files call sites (Exports, Analytics, IngestionQueries) now use an identical Succeeded + exists + contains shape, which is worth more than the individual hunks.
One non-blocking ask left in the second thread: the description implies both call sites were failing the same way, and they weren't — Exports genuinely hits a missing destination folder on first run, Analytics doesn't. A sentence there would save the next reader the trace. Approving regardless.
|
Hubs (no data) deployment failed. View logs. |
There was a problem hiding this comment.
Holding for validation
|
Hubs + ADX (managed) deployment failed. View logs. |
|
Hubs (manual) deployment failed. View logs. |
|
Hubs + ADX (managed) deployment failed. View logs. |
|
Hubs (manual) deployment failed. View logs. |
🛠️ Description
The "Get Existing Parquet Files"
GetMetadataactivity fails withPathNotFoundwhen the destination folder doesn't exist yet (e.g. first ingestion for a scope/dataset/month combination). Reservation recommendation exports hit this most often because their destination path adds an extraexportNamesegment, but any export type can hit it on a first run.Fixed in both places this pattern exists:
msexports_ETL_ingestion(Microsoft.CostManagement/Exports/app.bicep) and the analytics ingestion ETL pipeline (Microsoft.FinOpsHubs/Analytics/app.bicep). The two call sites aren't failing for the same reason, though: Exports writes to a destination folder that legitimately may not exist yet on first run, so this is a genuine fix there. Analytics reads a source folder that a manifest file just landed in, so that folder exists by construction whenever the pipeline runs — the change there is diagnostic, replacing an opaquePathNotFoundADLS error with the pipeline's ownIngestionFilesNotFounderror code if the folder is ever missing for some other reason (e.g. a malformedfolderPath). It does not make a missing ingestion folder a no-op.Fix: add
'exists'to theGetMetadataactivity'sfieldList. Per ADF's GetMetadata docs, specifyingexistsmakes the activity returnexists: falseinstead of throwing when the path is missing — "Ifexistsisn't specified in the field list, the Get Metadata activity fails if the object isn't found." The downstreamFilteractivity'sitemsexpression now checks.output.existsbefore reading.output.childItems, so a missing folder resolves to an empty list instead of propagating the failure.This is the same idiom already used elsewhere in this codebase for "path may not exist yet" scenarios:
Check SchemainExports/app.bicep, and bothGetMetadataactivities inIngestionQueries/app.bicep(one of which — "Get Existing Parquet Files" — already combinesexists+childItemsexactly like this fix does).Root cause verification (re: issue #2088 comment)
A prior comment on the issue did solid root-cause analysis but its line numbers had drifted and its proposed fix options were speculative (custom error handling / parent-folder existence checks / wrapping in an If Condition). Re-verified against the current code:
Microsoft.CostManagement/Exports/app.bicep:GetMetadataactivity is at lines 1090-1122,Filter Out Current Exportsat 1123-1147 (comment's estimate of ~1090-1122 / ~1123-1147 was accurate).Microsoft.FinOpsHubs/Analytics/app.bicep: identical vulnerable pattern confirmed at lines 1668-1707 (comment said ~1667-1706, essentially correct).existsinfieldList), not a custom If Condition or Web-activity workaround — and this idiom was already present elsewhere in the repo, so this change makes the two vulnerable call sites consistent with the rest of the codebase rather than introducing a new pattern.Fixes #2088
📷 Screenshots
Not applicable — backend ETL pipeline fix, no UI changes.
📋 Checklist
🔬 How did you test this change?
bicep build src/templates/finops-hub/modules/Microsoft.CostManagement/Exports/app.bicep --stdout— builds cleanlybicep build src/templates/finops-hub/modules/Microsoft.FinOpsHubs/Analytics/app.bicep --stdout— builds cleanly"exists"in bothfieldListarrayspwsh -Command "./src/scripts/Test-PowerShell.ps1 -Lint"— 3418/3418 passed📦 Deploy to test?
🙋♀️ Do any of the following that apply?
📑 Did you update
docs/changelog.md?📖 Did you update documentation?