Skip to content

Accept root-relative internal relationship targets (fixes silent shared-string loss on Mono) - #88

Merged
rabanti-github merged 1 commit into
rabanti-github:dev-prfrom
yolkin-games:mono-rootrelative-target
Aug 25, 2026
Merged

rabanti-github merged 1 commit into
rabanti-github:dev-prfrom
yolkin-games:mono-rootrelative-target

Conversation

@yolkin-games

@yolkin-games yolkin-games commented Aug 25, 2026

Copy link
Copy Markdown

Hey, I've updated to the new release today, and stumbled upon a smal hiccup with linux Mono build.

Summary

On Mono, 3.2.0 silently returns every string cell as its raw shared-string index instead of its text. No exception is thrown, the worksheet count is right, the cell count is right and numeric cells are right — only the strings are wrong. 3.1.0 is unaffected. .NET is unaffected on both Windows and Linux.

This matters for Unity, whose editor runs Mono: my item-balance importer reads an Excel-authored workbook at build time and got 0, 64, 133, 132 where id, value, domain, description belong. I only noticed because my own code hard-fails on a missing header — a more permissive consumer would have written the indices straight into its output.

Root cause

DiscoveryReader.ParseRelationship rejects an internal relationship whose target is an absolute URI, and decides that with Uri.IsAbsoluteUri:

if (targetUri.IsAbsoluteUri)
{
    return HandleRelationshipIssue(relationshipPartPath, id, "An internal relationship target cannot be an absolute URI.", issues, null);
}

Uri.IsAbsoluteUri disagrees between runtimes for a root-relative path:

Target .NET 8/10 Mono 6.12
sharedStrings.xml false false
/xl/sharedStrings.xml false true
http://example.com/x.xml true true

A leading-slash target is a root-relative path, which OPC permits for an internal relationship — it is not an absolute URI in the sense this check intends. On Mono the relationship is therefore discarded into Issues, and since XlsxReader looks up shared strings as an optional relationship:

RelationshipInfo sharedStringsRelationship = GetRelationship(relationshipCatalog, workbookPartPath, sharedStringsReader.DocumentType, false);
if (sharedStringsRelationship != null) { /* read shared strings */ }

…the part is skipped with no diagnostic, and every t="s" cell keeps its index.

Why this is not exotic

Microsoft Excel writes exactly this shape. In my workbook (Excel 16.0, AppVersion 16.0300), 26 of 27 relationships are plain relative and the sharedStrings one is /xl/sharedStrings.xml. So any Excel-authored workbook can trip it, and only on Mono.

The fix

Test for a real scheme rather than trusting IsAbsoluteUri, and rebuild the target as relative so PackUriHelper.ResolvePartUri agrees on both runtimes:

bool rootRelativeTarget = target.StartsWith("/", StringComparison.Ordinal);
if (targetUri.IsAbsoluteUri && !rootRelativeTarget)
{
    return HandleRelationshipIssue(relationshipPartPath, id, "An internal relationship target cannot be an absolute URI.", issues, null);
}
if (rootRelativeTarget)
{
    targetUri = new Uri(target, UriKind.Relative);
}

A genuinely absolute target (http://…) is still rejected. On .NET the change is a no-op, since IsAbsoluteUri was already false for these targets.

Verification

  • Mono 6.12 / Linux, stock 3.2.0 → config headers read back as 0, 64, 133, 132. With this patch → id, value, domain, description.
  • .NET 10 / Linux and Windows, before and after → id, value, domain, description (no behaviour change).
  • Deterministic: 8/8 runs, and unchanged when pinned to a single CPU, so it is not a scheduling race.
  • Verified against the shipped lib/netstandard2.0 assemblies plus System.IO.Packaging 4.7.0.

Minimal repro

Read any Excel-authored .xlsx whose xl/_rels/workbook.xml.rels carries Target="/xl/sharedStrings.xml", under Mono:

using var fs = File.OpenRead(path);
Workbook wb = WorkbookReader.Load(fs);
var sheet = wb.Worksheets.First();
Console.WriteLine(sheet.GetCell(0, 0).Value);   // 3.2.0 on Mono: an integer index. Expected: the header text.

Suggested follow-up (not in this PR)

The failure was silent because shared strings are optional and a dropped relationship only lands in RelationshipCatalog.Issues, which a caller never sees. Consider surfacing discovery issues for parts the reader then skips — a workbook that has a sharedStrings.xml part but no usable relationship to it is almost certainly a bug worth reporting rather than reading past.


Same reporter as the plug-in loader guard shipped in 3.2.0 — thanks for taking that one.

An internal relationship whose Target begins with '/' is a root-relative
path, which OPC permits. ParseRelationship rejected it because
Uri.IsAbsoluteUri disagrees across runtimes for that shape: .NET parses
"/xl/sharedStrings.xml" as a RELATIVE Uri, Mono parses it as ABSOLUTE.

On Mono the relationship is therefore discarded as "an internal
relationship target cannot be an absolute URI". Because sharedStrings is
looked up as an optional relationship, the reader then skips the shared
strings part silently and every string cell is returned holding its raw
shared-string INDEX instead of its text -- no exception, correct cell
count, wrong values.

Microsoft Excel writes exactly this shape: in a workbook produced by
Excel 16.0, 26 of 27 relationships are plain relative and the
sharedStrings one is "/xl/sharedStrings.xml", so any Excel-authored
workbook can hit it. Reproduced on Mono 6.12 (Unity's editor runtime);
.NET 8/10 are unaffected.

Test for a real scheme rather than trusting IsAbsoluteUri, and rebuild
the target as relative so PackUriHelper.ResolvePartUri agrees on both
runtimes. A genuinely absolute target (http://...) is still rejected.
@rabanti-github
rabanti-github changed the base branch from master to dev-pr August 25, 2026 11:17
@rabanti-github rabanti-github added the code review A PR or patch is recently reviewed label Aug 25, 2026
@rabanti-github
rabanti-github merged commit e25d805 into rabanti-github:dev-pr Aug 25, 2026
1 of 2 checks passed
@rabanti-github rabanti-github added completed A bug was fixed or question answered and the issue will be closed soon and removed code review A PR or patch is recently reviewed labels Aug 25, 2026
@rabanti-github

Copy link
Copy Markdown
Owner

Thanks for the bugfix.
The new release 3.2.1 is already available

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

completed A bug was fixed or question answered and the issue will be closed soon

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants