siyuan-note/siyuan · error

rollup requires a configured relation field

Error message

rollup requires a configured relation field

What it means

A rollup aggregates values through a relation field in the same attribute view. This error is thrown when the relationKeyID in the rollup config cannot be resolved (GetKey error), resolves to a non-relation key, or the key has no Relation configured — so there is no valid relation field to walk for aggregation.

Solutions

  1. Point relationKeyID at an existing relation key in the same attribute view that has a configured target (Relation.AvID set)
  2. Recreate the relation field if it was deleted, then retry the rollup update
  3. Validate with attrView.GetKey(relationKeyID) that the key exists, is KeyTypeRelation, and has non-nil Relation before calling

Example fix

// before
payload := map[string]any{"relationKeyID": deletedRelID, "keyID": kID, "operator": "Count"}
// after
if rk, err := attrView.GetKey(relID); err == nil && rk.Type == av.KeyTypeRelation && rk.Relation != nil {
    payload := map[string]any{"relationKeyID": relID, "keyID": kID, "operator": "Count"}
}
Defensive patterns

Strategy: validation

Validate before calling

const relKey = av.keys.find(k => k.id === relationKeyID);
const ok = relKey && relKey.type === 'relation' && relKey.relation && relKey.relation.avID;
if (!ok) throw new Error('rollup needs a configured relation field');

Type guard

function isConfiguredRelationKey(key) {
  return !!key && key.type === 'relation' && !!key.relation && !!key.relation.avID;
}

Try / catch

try {
  await api.updateAttributeViewKeyConfig({avID, keyID, rollup: cfg});
} catch (e) {
  if (e.message.includes('configured relation field')) {
    // re-point relationKeyID at a valid relation key
  }
}

Prevention

When it happens

Trigger: Calling UpdateAttributeViewKeyConfig with rollup config whose relationKeyID points to a deleted key, a non-relation key, or a relation key that has no target view set (Relation == nil).

Common situations: The referenced relation field was deleted or converted to another type after the rollup config was drafted; relationKeyID copied from another view; a relation key created but never assigned a target avID.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/62e46498bc5e1604. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/attribute_view_key_config.go:334

func updateAttributeViewKeyRollup(attrView *av.AttributeView, key *av.Key, op *Operation, value any) error {
	if av.KeyTypeRollup != key.Type {
		return errors.New("rollup requires a rollup field")
	}
	var config struct {
		RelationKeyID string  `json:"relationKeyID"`
		KeyID         string  `json:"keyID"`
		Operator      *string `json:"operator"`
	}
	if err := decodeAttributeViewKeyConfig(value, &config); nil != err {
		return err
	}
	if nil == config.Operator || !slices.Contains(AttributeViewKeyRollupOperators, *config.Operator) {
		return errors.New("rollup requires a supported operator")
	}
	relationKey, err := attrView.GetKey(config.RelationKeyID)
	if nil != err || av.KeyTypeRelation != relationKey.Type || nil == relationKey.Relation {
		return errors.New("rollup requires a configured relation field")
	}
	dest, err := av.ParseAttributeView(relationKey.Relation.AvID)
	if nil != err {
		return err
	}
	if _, err = dest.GetKey(config.KeyID); nil != err {
		return err
	}
	calc := &av.RollupCalc{Operator: av.CalcOperator(*config.Operator)}
	op.ParentID, op.KeyID = config.RelationKeyID, config.KeyID
	op.Data = map[string]any{"calc": calc}
	return updateAttributeViewColRollup(op)
}

View on GitHub (pinned to 9f775e8a12)