spectreconsole/spectre.console · error · InvalidOperationException
A link has already been set.
Error message
A link has already been set.
What it means
Inside the private AnsiMarkupTagParser.Parse, a second `link=...` clause is encountered when effectiveLink is already set. The error string is returned and re-thrown by the public Parse as InvalidOperationException. A bare `link` (auto-link) does not conflict, only explicit `link=` does.
Source
Thrown at src/Spectre.Console.Ansi/AnsiMarkupTagParser.cs:10
namespace Spectre.Console;
internal static class AnsiMarkupTagParser
{
public static (Style Style, Link? Link) Parse(string text)
{
var result = Parse(text, out var error);
if (error != null)
{
throw new InvalidOperationException(error);
}
return result ?? throw new InvalidOperationException("Could not parse style.");
}
public static bool TryParse(string text, [NotNullWhen(true)] out (Style Style, Link?)? result)
{
result = Parse(text, out _);
return result != null;
}
private static (Style Style, Link? Link)? Parse(string text, out string? error)
{
var effectiveDecoration = (Decoration?)null;
var effectiveForeground = (Color?)null;
var effectiveBackground = (Color?)null;
var effectiveLink = (string?)null;
View on GitHub (pinned to 0acc92fada)
Solutions
- Use only one `link=` per tag
- Split into separate tags if two different links are genuinely needed
- For an auto-link (URL derived from text), use bare `link` instead of `link=`
Example fix
// before
AnsiMarkup.Parse("[link=http://a.com link=http://b.com]click[/]");
// after
AnsiMarkup.Parse("[link=http://a.com]click[/]"); Defensive patterns
Strategy: validation
Validate before calling
// Ensure at most one link= clause per tag:
public static bool HasSingleLink(string tag)
=> tag.Split(' ').Count(p => p.StartsWith("link=", StringComparison.OrdinalIgnoreCase)) <= 1; Try / catch
try { var segments = AnsiMarkup.Parse(markup); }
catch (InvalidOperationException ex) when (ex.Message == "A link has already been set.")
{ /* rebuild the tag with a single link= */ } Prevention
- Define links once per tag; reuse bare `link` for auto-links
- When merging style fragments, deduplicate link= attributes
- Prefer composing separate tags over cramming multiple links into one
When it happens
Trigger: A single tag containing two `link=` clauses, e.g. `[link=http://a.com link=http://b.com]text[/]`, passed to AnsiMarkup.Parse/Write.
Common situations: Merging two styled spans and accidentally concatenating their link attributes; templating that appends a default link to a tag that already has one.
Related errors
- Encountered closing tag when none was expected near position
- Encountered unknown markup token.
- Unbalanced markup stack. Did you forget to close a tag?
- Encountered unescaped ']' token at position {_reader.Positio
- Encountered malformed markup tag at position {pos}.
AI-assisted analysis of spectreconsole/spectre.console@0acc92fada (2026-08-13).
Data as JSON: /api/errors/59465e2bec22d722.
Report an issue: GitHub.