siyuan-note/siyuan · error
cannot remove primary key field
Error message
cannot remove primary key field
What it means
removeAttributeViewField refuses to delete a database (attribute view) field whose type is av.KeyTypeBlock, i.e. the primary key / block-name column. The primary key is the structural anchor that maps rows to real document blocks, so the kernel treats it as permanently non-removable and returns this plain error instead of deleting the field.
Solutions
- Check key.Type before removing: only issue removeAttrViewColumn for fields whose type is not av.KeyTypeBlock
- If the goal is hiding rather than deleting, remove or hide the corresponding view table column (ViewTableColumn) instead of deleting the field definition
- If the whole database is unwanted, delete the attribute view itself rather than its primary key field
Example fix
// before
ops := []*model.Operation{{Action: "removeAttrViewColumn", AvID: avID, ID: colID}}
// after
avObj, _ := model.ParseAttributeView(avID)
key, _ := avObj.GetKey(colID)
if key != nil && key.Type != av.KeyTypeBlock {
ops := []*model.Operation{{Action: "removeAttrViewColumn", AvID: avID, ID: colID}}
} Defensive patterns
Strategy: validation
Validate before calling
av, err := model.ParseAttributeView(avID)
if err != nil { return err }
key, err := av.GetKey(fieldID)
if err != nil { return err }
if key.Type == av.KeyTypeBlock {
return fmt.Errorf("field %s is the primary key and cannot be removed", fieldID)
} Type guard
func isRemovableField(k *av.Key) bool { return k != nil && k.Type != av.KeyTypeBlock } Try / catch
if err := tx.removeAttributeViewField(op); err != nil {
if err.Error() == "cannot remove primary key field" {
// hide column in view instead of deleting
return nil
}
return err
} Prevention
- Always read key.Type before issuing removeAttrViewColumn
- Treat the block-type column as immutable; operate on view columns for visibility changes
- Favor the official frontend/plugin APIs, which already filter out the primary key
When it happens
Trigger: Calling removeAttributeViewField (exposed via the removeAttrViewColumn transaction action or RemoveAttributeViewKey API) with op.ID pointing at the field of type KeyTypeBlock in the target attribute view.
Common situations: A plugin or script tries to delete 'the first column' of a database programmatically without checking its type; a UI integration passes a column id obtained from a table layout where the block column is listed alongside normal columns; an API consumer misidentifies the row-title column as an ordinary text field.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- database history contains an invalid group
- database history contains an invalid view
- database history has no primary field
- at least one visible view is required
- attribute view custom color index
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/0ee4cbfc5cef637c.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/attribute_view_fields.go:109
return nil, loadErr
}
copy, copyErr := cloneAttributeViewForFieldMutation(current)
if copyErr != nil {
return nil, copyErr
}
before[id], views[id] = copy, current
return current, nil
}
source, err := load(op.AvID, false)
if err != nil {
return err
}
key, err := source.GetKey(op.ID)
if err != nil {
return err
}
if key.Type == av.KeyTypeBlock {
return errors.New("cannot remove primary key field")
}
removals := map[string][]string{source.ID: {key.ID}}
if relation := key.Relation; key.Type == av.KeyTypeRelation && relation != nil && relation.IsTwoWay && relation.AvID != "" {
dest, loadErr := load(relation.AvID, true)
if loadErr != nil {
return loadErr
}
if dest != nil {
back, _ := dest.GetKey(relation.BackKeyID)
if back != nil && back.Type == av.KeyTypeRelation && back.Relation != nil &&
back.Relation.AvID == source.ID && back.Relation.IsTwoWay {
if back.Relation.BackKeyID != key.ID {
return fmt.Errorf("database back relation field [%s] has changed", back.ID)
}
if op.RemoveDest {
removals[dest.ID] = append(removals[dest.ID], back.ID)
} else {
back.Relation.IsTwoWay, back.Relation.BackKeyID = false, ""View on GitHub (pinned to 9f775e8a12)