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

  1. 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.
  2. Compare the %T from the message against the %s type string to find the mismatched pair.
  3. Use the library constructors/helpers for nodes instead of setting Type and Attributes independently.
  4. 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

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


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)