iOfficeAI/OfficeCLI · error · ArgumentException

Invalid 'secondaryAxis' value: '{value}'. Valid forms: index

Error message

Invalid 'secondaryAxis' value: '{value}'. Valid forms: index ('2'), comma index list ('2,3'), 'true', 'false'/'none', seriesN alias ('series2'), or a chart-type name on a combo chart ('line').

What it means

Thrown when 'secondaryAxis' does not match any accepted form after all parses fail: it is not 'true', not 'false'/'none'/blank, not a comma list of positive integer indices, not a 'seriesN' alias list, and not a chart-type name present on a combo chart (SeriesIndicesForChartType returned empty). This is the R47 guard that replaced the old silent no-op behavior.

Source

Thrown at src/officecli/Core/Chart/ChartHelper.Setter.cs:1725

                                var trimmed = s.Trim();
                                if (int.TryParse(trimmed, out var v)) return v;
                                var m = System.Text.RegularExpressions.Regex.Match(
                                    trimmed, @"^series(\d+)$",
                                    System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                                return m.Success && int.TryParse(m.Groups[1].Value, out var sv) ? sv : -1;
                            })
                            .Where(v => v > 0).ToHashSet();
                        // Type-name form, e.g. "line" on a combo chart — route every
                        // series whose parent CT_*Chart element matches that type to
                        // the secondary axis. Without this, "line" parsed to an empty
                        // index set and silently no-op'd, leaving the lineChart bound
                        // to the primary axId (the R26 combo bug).
                        if (secondaryIndices.Count == 0)
                            secondaryIndices = SeriesIndicesForChartType(plotArea2, value);
                        // R47: still empty → value was not a valid index, type name,
                        // or seriesN alias. Throw instead of silent no-op.
                        if (secondaryIndices.Count == 0)
                            throw new ArgumentException(
                                $"Invalid 'secondaryAxis' value: '{value}'. Valid forms: " +
                                "index ('2'), comma index list ('2,3'), 'true', 'false'/'none', " +
                                "seriesN alias ('series2'), or a chart-type name on a combo chart ('line').");
                    }
                    ApplySecondaryAxis(plotArea2, secondaryIndices);
                    break;
                }

                case "plotarea.x" or "plotarea.y" or "plotarea.w" or "plotarea.h":
                {
                    if (!double.TryParse(value, System.Globalization.NumberStyles.Float,
                        System.Globalization.CultureInfo.InvariantCulture, out var layoutVal)
                        || !double.IsFinite(layoutVal))
                    { unsupported.Add(key); break; }
                    var plotArea3 = chart.GetFirstChild<C.PlotArea>();
                    if (plotArea3 == null) { unsupported.Add(key); break; }
                    SetManualLayoutProperty(plotArea3, key.Split('.')[1].ToLowerInvariant(), layoutVal, isPlotArea: true);
                    break;

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Use a 1-based index or comma list ('2', '2,3'), 'series2' alias, 'true', 'false'/'none', or a chart-type name that exists on the combo chart.
  2. For zero-based UIs, convert: send (uiIndex + 1).
  3. Verify the type-name against the chart's actual CT_*Chart elements before sending.

Example fix

// before
SetChartProperties(part, new() { ["secondaryAxis"] = "0" });
// after
SetChartProperties(part, new() { ["secondaryAxis"] = (uiIndex + 1).ToString() });
Defensive patterns

Strategy: validation

Validate before calling

static readonly Regex SeriesAlias = new(@"^series(\d+)$", RegexOptions.IgnoreCase);

static bool IsValidSecondaryAxis(string v)
{
    if (string.IsNullOrWhiteSpace(v)) return true; // no-op
    if (v.Equals("true", StringComparison.OrdinalIgnoreCase)) return true;
    if (v.Equals("false", StringComparison.OrdinalIgnoreCase)) return true;
    if (v.Equals("none", StringComparison.OrdinalIgnoreCase)) return true;
    foreach (var part in v.Split(','))
    {
        var t = part.Trim();
        if (int.TryParse(t, out var n) && n > 0) continue;
        if (SeriesAlias.IsMatch(t)) continue;
        return false; // type-name form must be validated against the chart's series
    }
    return true;
}

Type guard

static bool IsIndexOrAlias(string v) =>
    (int.TryParse(v, out var n) && n > 0)
    || SeriesAlias.IsMatch(v);

Try / catch

try { SetChartProperties(part, props); }
catch (ArgumentException ex) when (ex.Message.StartsWith("Invalid 'secondaryAxis'"))
{ /* surface accepted forms to the user; indices are 1-based */ }

Prevention

When it happens

Trigger: SetChartProperties with { ["secondaryAxis"] = "all" }, "second", "0" (zero is filtered out), or a type name like 'area' on a chart that has no area series.

Common situations: Free-text 'secondary' from a UI; zero-based index sent as 0 (indices are 1-based, 0 is dropped); type-name form used on a single-type (non-combo) chart where no series matches.

Related errors


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