Skip to content

TableauFileEditor.GetXmlStream() throws 'InvalidOperationException' for packaged workbooks with multiple embedded/local data sources #96

Description

@jyoung-slalom

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

  1. 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).
  2. Publish/save as a packaged workbook (.twbx).
  3. Migrate the workbook using the Migration SDK (default behavior, or via a custom IXmlContentTransformer<TPublish> hook — the underlying GetXmlStream() call happens regardless).
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions