iOfficeAI/OfficeCLI · error · ArgumentException

drawing-group hyperlink entries require non-empty Id and Tar

Error message

drawing-group hyperlink entries require non-empty Id and Target.

What it means

After the hyperlinks carrier decoded successfully, at least one entry has an empty Id or Target. The handler needs both because it calls AddHyperlinkRelationship(Target) and then RemapDrawingRelationshipId(anchor, hyperlink.Id, ...) to rewrite the source rId in the verbatim anchor. An empty value would create a useless relationship and/or a no-op remap.

Source

Thrown at src/officecli/Handlers/Excel/ExcelHandler.Add.cs:1177

                    groupDrawingsPart.WorksheetDrawing = new XDR.WorksheetDrawing();
                    groupDrawingsPart.WorksheetDrawing.Save();
                }
                var groupSheet = GetSheet(groupWorksheet);
                if (groupSheet.GetFirstChild<SpreadsheetDrawing>() == null)
                {
                    var drawingRelId = groupWorksheet.GetIdOfPart(groupDrawingsPart);
                    groupSheet.Append(new SpreadsheetDrawing { Id = drawingRelId });
                    SaveWorksheet(groupWorksheet);
                }

                // Relationship IDs are scoped to the destination drawing part.
                // Create fresh IDs (avoids collisions with pictures/charts
                // emitted earlier), then rewrite every r:id/r:embed/r:link in
                // the verbatim group anchor that referenced the source ID.
                foreach (var hyperlink in groupHyperlinks)
                {
                    if (string.IsNullOrEmpty(hyperlink.Id) || string.IsNullOrEmpty(hyperlink.Target))
                        throw new ArgumentException(
                            "drawing-group hyperlink entries require non-empty Id and Target.");
                    var uri = new Uri(hyperlink.Target, UriKind.RelativeOrAbsolute);
                    var replayRel = groupDrawingsPart.AddHyperlinkRelationship(
                        uri, hyperlink.IsExternal);
                    RemapDrawingRelationshipId(groupAnchor, hyperlink.Id, replayRel.Id);
                }

                groupDrawingsPart.WorksheetDrawing.AppendChild(groupAnchor);
                groupDrawingsPart.WorksheetDrawing.Save();
                var groupIndex = groupDrawingsPart.WorksheetDrawing
                    .Elements<XDR.TwoCellAnchor>()
                    .Count(a => a.GetFirstChild<XDR.GroupShape>() != null);
                return ("group", $"/{groupSheetName}/group[{groupIndex}]");
            }

            case "chartex":
            {
                // Extended (cx:) chart carrier for dump→batch round-trip.

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Filter out hyperlinks with empty Id or Target before encoding the carrier.
  2. Re-dump so only resolved hyperlinks (with both r:id and Target) are emitted.
  3. If the source truly lacks a Target, drop that hyperlink entry — it cannot be replayed.

Example fix

// before — empty Target slips through
var specs = new List<DumpDrawingHyperlinkSpec>{
    new(){ Id="rId2", Target="", IsExternal=true }};
props["hyperlinks"] = EncodeDumpDrawingHyperlinks(specs);
// after — drop incomplete entries before encoding
props["hyperlinks"] = EncodeDumpDrawingHyperlinks(
    specs.Where(h => !string.IsNullOrEmpty(h.Id) && !string.IsNullOrEmpty(h.Target)));
Defensive patterns

Strategy: validation

Validate before calling

var specs = DecodeDumpDrawingHyperlinks(props.GetValueOrDefault("hyperlinks") ?? "");
if (specs.Any(h => string.IsNullOrEmpty(h.Id) || string.IsNullOrEmpty(h.Target)))
    /* drop incomplete entries before encoding */

Type guard

static bool HyperlinksAllComplete(string encoded) =>
    DecodeDumpDrawingHyperlinks(encoded).All(h =>
        !string.IsNullOrEmpty(h.Id) && !string.IsNullOrEmpty(h.Target));

Try / catch

try { handler.AddPart(parent, "drawing-group", props); }
catch (ArgumentException ex) when (ex.Message.Contains("non-empty Id and Target"))
{ /* filter specs, re-encode, retry */ }

Prevention

When it happens

Trigger: A decoded entry whose base64 decoded to an empty string for Id or Target — e.g. the source anchor had a hyperlink with no r:id, or the carrier was authored with empty base64 (" , ,1").

Common situations: Source drawing contained a dangling hyperlink element with missing attributes; a dump that emitted a hyperlink before its relationship was resolved; hand-written carrier with placeholder empty fields.

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/3664dcd4dea6dc39. Report an issue: GitHub.