ory/kratos · error
node type and node attributes mismatch: %T !=
Error message
node type and node attributes mismatch: %T != %s
What it means
In MarshalJSON, the type derived from the concrete Attributes struct is compared against the value already set on n.Type. If they differ, the node is internally inconsistent — its declared type does not match its attributes — so serialization aborts to avoid emitting a corrupt payload.
Solutions
- Set n.Type to the string constant matching the attributes struct (e.g. Input with *InputAttributes), or clear n.Type and let MarshalJSON derive it.
- Compare the %T from the message against the %s type string to find the mismatched pair.
- Use the library constructors/helpers for nodes instead of setting Type and Attributes independently.
- When copying nodes, copy Type and Attributes together as a unit.
Example fix
// before
n := ui.Node{Type: ui.TypeInput, Attributes: &ui.TextAttributes{}}
// after
n := ui.Node{Type: ui.TypeText, Attributes: &ui.TextAttributes{NodeType: ui.Text}} Defensive patterns
Strategy: validation
Validate before calling
var expected ui.NodeType
switch a := n.Attributes.(type) {
case *ui.InputAttributes:
expected = ui.Input
case *ui.TextAttributes:
expected = ui.Text
// ... other cases
default:
return
}
if n.Type != "" && n.Type != expected {
// fix or clear n.Type before marshaling
} Try / catch
if _, err := json.Marshal(node); err != nil && strings.Contains(err.Error(), "node type and node attributes mismatch") {
log.Error("inconsistent node", "err", err) // Type field disagrees with Attributes
} Prevention
- Construct nodes via helpers or set Attributes first and let MarshalJSON derive Type (leave Type empty).
- When mutating parsed nodes, change Type and Attributes together.
- Add round-trip tests for hand-assembled nodes.
When it happens
Trigger: Setting n.Type = "input" while assigning n.Attributes = &TextAttributes{...} (or any mismatched pair), then marshaling the node; deserializing into a Node and manually changing Type before re-serializing.
Common situations: Hand-assembling flow UI nodes in tests or custom renderers; mapping external data into Node structs where Type is copied from one node and Attributes from another; mutating a parsed node's Type without updating Attributes.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- unknown node type: %T
- unexpected node type
- : Could not marshal the traits schema. This usually means…
- failed to unmarshal webhook configuration for
- failed to unmarshal notify_previous_addresses configuration…
AI-assisted analysis of ory/kratos@b86338da04 (2026-09-07).
Data as JSON: /api/errors/eca2de79d4e3e6b2.
Report an issue: GitHub.
Appendix: source
Thrown at ui/node/node.go:569
attr.NodeType = Anchor
case *ImageAttributes:
t = Image
attr.NodeType = Image
case *ScriptAttributes:
t = Script
attr.NodeType = Script
case *DivisionAttributes:
t = Division
attr.NodeType = Division
default:
return nil, errors.WithStack(fmt.Errorf("unknown node type: %T", n.Attributes))
}
}
if n.Type == "" {
n.Type = t
} else if n.Type != t {
return nil, errors.WithStack(fmt.Errorf("node type and node attributes mismatch: %T != %s", n.Attributes, n.Type))
}
if n.Meta == nil {
n.Meta = new(Meta)
}
return json.Marshal((*jsonRawNode)(n))
}
View on GitHub (pinned to b86338da04)