Environment
- Tableau Migration SDK version: 6.1.0
- Language: Python
- Content type affected: Workbook (
.twbx) publish
Summary
Publishing a .twbx workbook that contains more than one embedded ("local") data source fails with an unhandled InvalidOperationException: Sequence contains more than one matching element. The failure occurs inside TableauFileEditor.GetXmlStream(), which uses LINQ's .Single() to locate the workbook's XML entry in the packaged zip archive, but matches on file extension only rather than on the entry's position within the archive. Any .tds file bundled with the package — including those nested in a Local Data subfolder alongside .hyper extracts — is incorrectly treated as a candidate match.
Steps to Reproduce
- In Tableau Desktop, create a workbook with more than one local/embedded data source (i.e., data sources added via a file-based or extract connection rather than "Published Data Source"), so the resulting
.twbx package contains multiple .tds files under Local Data/ alongside the corresponding .hyper file(s).
- Publish/save as a packaged workbook (
.twbx).
- Migrate the workbook using the Migration SDK (default behavior, or via a custom
IXmlContentTransformer<TPublish> hook — the underlying GetXmlStream() call happens regardless).
- Observe the migration manifest records an error for the item.
Expected Behavior
The workbook's own .twb XML entry is located and returned for transformation, regardless of how many .tds files exist elsewhere in the package.
Actual Behavior
Migration fails for the item with:
System.InvalidOperationException: Sequence contains more than one matching element
at System.Linq.ThrowHelper.ThrowMoreThanOneMatchException()
at System.Linq.Enumerable.TryGetSingle[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source, Func`2 predicate)
at Tableau.Migration.Content.Files.TableauFileEditor.GetXmlStream()
at Tableau.Migration.Content.Files.ContentFileHandle.GetXmlStreamAsync(CancellationToken cancel)
at Tableau.Migration.Engine.Hooks.Transformers.IXmlContentTransformer`1.Tableau.Migration.Engine.Hooks.IMigrationHook<TPublish>.ExecuteAsync(TPublish ctx, CancellationToken cancel)
at Tableau.Migration.Engine.Hooks.MigrationHookRunnerBase.ExecuteAsync[THook,TContext](TContext context, Action`3 afterHookAction, CancellationToken cancel)
at Tableau.Migration.Engine.Hooks.Transformers.ContentTransformerRunner.ExecuteAsync[TPublish](TPublish itemToTransform, CancellationToken cancel)
at Tableau.Migration.Engine.Preparation.ContentItemPreparerBase`3.TransformAsync(TPublish publishItem, CancellationToken cancel)
at Tableau.Migration.Engine.Preparation.ContentItemPreparerBase`3.PreparePublishItemAsync(ContentMigrationItem`1 item, TPublish publishItem, CancellationToken cancel)
at Tableau.Migration.Engine.Preparation.ContentItemPreparerBase`3.PrepareAsync(ContentMigrationItem`1 item, CancellationToken cancel)
at Tableau.Migration.Engine.Migrators.Batch.ContentBatchMigratorBase`3.MigrateBatchItemAsync(ContentMigrationItem`1 item, ContentMigrationBatch`2 batch)
Workaround
None identified on the consumer side — GetXmlStream() is invoked internally by the SDK's own file-handling/transform pipeline (ContentFileHandle.GetXmlStreamAsync), so it cannot be bypassed from a custom IXmlContentTransformer hook. Workbooks with 2+ embedded/local data sources currently cannot be migrated.
Notes
This isn't an error I've encountered in previous migrations; however, with the current migration I'm executing, it's impacted 20 workbooks on a single site. Uncertain if this is just an archaic workbook structure that is preserved, but workbooks function on Tableau Server so the structure seems to be valid.
Environment
.twbx) publishSummary
Publishing a
.twbxworkbook that contains more than one embedded ("local") data source fails with an unhandledInvalidOperationException: Sequence contains more than one matching element. The failure occurs insideTableauFileEditor.GetXmlStream(), which uses LINQ's.Single()to locate the workbook's XML entry in the packaged zip archive, but matches on file extension only rather than on the entry's position within the archive. Any.tdsfile bundled with the package — including those nested in aLocal Datasubfolder alongside.hyperextracts — is incorrectly treated as a candidate match.Steps to Reproduce
.twbxpackage contains multiple.tdsfiles underLocal Data/alongside the corresponding.hyperfile(s)..twbx).IXmlContentTransformer<TPublish>hook — the underlyingGetXmlStream()call happens regardless).Expected Behavior
The workbook's own
.twbXML entry is located and returned for transformation, regardless of how many.tdsfiles exist elsewhere in the package.Actual Behavior
Migration fails for the item with:
Workaround
None identified on the consumer side —
GetXmlStream()is invoked internally by the SDK's own file-handling/transform pipeline (ContentFileHandle.GetXmlStreamAsync), so it cannot be bypassed from a customIXmlContentTransformerhook. Workbooks with 2+ embedded/local data sources currently cannot be migrated.Notes
This isn't an error I've encountered in previous migrations; however, with the current migration I'm executing, it's impacted 20 workbooks on a single site. Uncertain if this is just an archaic workbook structure that is preserved, but workbooks function on Tableau Server so the structure seems to be valid.