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

  1. Check key.Type before removing: only issue removeAttrViewColumn for fields whose type is not av.KeyTypeBlock
  2. If the goal is hiding rather than deleting, remove or hide the corresponding view table column (ViewTableColumn) instead of deleting the field definition
  3. 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

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


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)