# Steps linked to a data attribute
Some older steps in Virtuoso are linked to a data attribute. When a step has this link, the value it uses at run time comes from a column in the journey's data table — not from the text you see in the step editor. The link is not always visible in the editor, so a step can behave differently from how it reads.
This linking mechanism is being removed. This page explains what the link does, how to tell whether a given step still needs it, and how to re-author the step so it keeps working once the link is gone.
If one of your journeys shows a warning banner about a data-attribute link — or a step in it carries a warning icon — this is the page they point to.
You only see the steps that need you
Where the link makes no difference to what a step does — the value it points at is the same one the step would use anyway, or the step is never actually reached — Virtuoso removes the link automatically and the step's behaviour does not change. That happens silently in the background — the step stays exactly as it is, and no warning is shown for it. A step is flagged only while its link cannot yet be removed safely on its own — so every flagged step is one to act on; there is nothing to sort through. The journey shows a banner listing the flagged steps and marks each with a warning icon. (The background clean-up keeps running, so a step flagged today may clear itself later — for example if its journey goes unused — and its warning then disappears. Where you can, re-author it rather than wait for that.) Your data tables, steps, and journeys are otherwise untouched.
If a flagged step goes unused for an extended period, its link is removed automatically and the step falls back to the text shown in it — which may not be what you want. Re-author flagged steps before that happens; if you need the exact inactivity period for your account, raise a support ticket.
# What the link does
When a step is linked to a data attribute, at run time Virtuoso replaces part of that step with the value from the linked column, for the row currently being executed. Which part it replaces depends on the kind of step:
- Write / Store — the value the step enters or saves comes from the linked column instead of the text shown in the step.
- Assert — the expected value the assertion checks against comes from the linked column, not the text shown after
equals. - Navigate — the URL comes from the linked column.
Because this can happen without being visible, a step like Write "MPI" in field … may actually enter a different value at run time — whatever the linked column holds for that row.
# Why these links cause trouble
The link ties a step to one specific column by name. That is fine as long as the step only ever runs against a data table that has that column. The problem is that the same step can be reused across several journeys, and those journeys can have different data tables:
- In journeys whose table has the column, the link resolves and the step uses that value.
- In journeys whose table does not have the column, the link has nothing to point at, and the step's behaviour there is not what the link implies.
So a single step ends up meaning two different things depending on which journey runs it — its value is driven by a column in some journeys ("same-table" journeys) and undefined in others ("divergent" journeys). This is the design flaw the removal is unwinding: what a step does should be visible in the step, not depend on which journey's table happens to contain a matching column.
# How to tell whether a step still needs the link
Open the flagged step and ask: does this step rely on a value from that data-table column, or should it use the text you see?
- If the step should use the value from the data table, that intent should be made explicit (see below) so it survives the link being removed.
- If the link is a leftover and the step should simply use the text shown in it, no data-table value is needed — the step will use that text once the link is gone.
Because the same step can belong to several journeys, checking a single run is not enough. Test the step across every journey it belongs to, and ideally data-driven — over the full set of data rows, not just one — so you catch the journeys whose table lacks the column. A step that looks correct in one journey can still misbehave in a divergent one.
# How to re-author each kind of step
The goal is to make the step read the data-table value explicitly, so its behaviour no longer depends on a hidden link.
# Write and Store
Reference the column by name in the step. If the column is Product, write the value as the variable ${Product} (or use the column directly in an expression) instead of relying on the link. The step then visibly uses the data-table value, and removing the link changes nothing.
# Assert
Put the expected value in its own column and compare against it explicitly. For example, rather than an assertion that silently expects a linked column, write:
Assert ${$ActualValue == $ExpectedValue} equals "true"
where $ExpectedValue is a column in the journey's data table. Both sides are now visible in the step.
If an assert step is hard to resolve — for example it is shared across journeys with different tables — you have more room than with other step types. An assert only checks a value; it does not feed a value into the rest of the journey the way Write, Store, or Navigate do. So an unresolved assert can be left set to ignore its outcome (or skipped in the journeys that lack the column, as below) without breaking the flow of the test, whereas an unresolved action step would.
# Navigate
Reference the URL column explicitly (for example as ${StartUrl}) rather than relying on the link.
# Steps shared across several journeys
This is the divergent-journey case described above: the flagged step is reused across journeys, and only some of their data tables contain the linked column. If you re-author the step to reference that column outright, the journeys that don't have it will error — an expression that reads a variable which was never provided fails rather than treating it as empty. Two safe options:
Referencing a column a journey may not have
Preferred — only run the step where the value exists. Add a condition so the step runs only in journeys whose table provides the column:
typeof $ExpectedValue !== 'undefined'The step then runs where the column exists and is skipped (not failed) elsewhere. Use
typeof …, not$ExpectedValue != null— the latter errors when the column is absent.Provide a fallback in the expression. If the step must run everywhere, guard the reference:
${$Actual == (typeof $ExpectedValue !== 'undefined' ? $ExpectedValue : 'default')}
The cleanest fix is usually the first one: don't assert (or act) in journeys that have nothing to compare against.
# Need help?
If you're unsure whether a flagged step relies on its link, or how to re-author it, raise a support ticket and reference this page. Include the affected journey (or step) so we can review the specific steps with you and confirm the right fix.