siyuan-note/siyuan · error

invalid width for attribute view column [%s]

Error message

invalid width for attribute view column [%s]

What it means

Thrown by setAttributeViewColsWidth when an individual value in the widths map cannot be asserted to string. Even though the overall Data is a map, each width entry must be a string value; numbers, booleans, or objects are rejected.

Source

Thrown at kernel/model/attribute_view.go:6343

func (tx *Transaction) doSetAttrViewColumnsWidth(operation *Operation) (ret *TxErr) {
	err := setAttributeViewColsWidth(operation)
	if err != nil {
		return &TxErr{code: TxErrHandleAttributeView, id: operation.AvID, msg: err.Error()}
	}
	return
}

func setAttributeViewColsWidth(operation *Operation) (err error) {
	widthData, ok := operation.Data.(map[string]any)
	if !ok {
		return fmt.Errorf("invalid attribute view column widths")
	}
	widths := map[string]string{}
	for id, value := range widthData {
		width, valueOK := value.(string)
		if !valueOK {
			return fmt.Errorf("invalid width for attribute view column [%s]", id)
		}
		widths[id] = width
	}

	attrView, err := av.ParseAttributeView(operation.AvID)
	if err != nil {
		return err
	}
	view, err := getAttrViewViewByBlockID(attrView, operation.BlockID)
	if err != nil {
		return err
	}
	if av.LayoutTypeTable != view.LayoutType {
		return nil
	}
	for _, column := range view.Table.Columns {
		if width, found := widths[column.ID]; found {
			column.Width = av.FilterWidthValue(width)

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Coerce every width to a string on the client before constructing the operation (String(width) / width.toString()).
  2. Omit entries whose width is null/undefined instead of sending them.
  3. Add a unit test that the widths payload is always Record<string, string>.

Example fix

// before
op.Data = { col1: 200, col2: '120px' } // mixed numeric/string
// after
op.Data = Object.fromEntries(
  Object.entries(widths).filter(([, w]) => w != null).map(([id, w]) => [id, String(w)])
)
Defensive patterns

Strategy: type-guard

Validate before calling

for (const [id, w] of Object.entries(op.data)) {
  if (typeof w !== 'string') op.data[id] = String(w) // coerce before sending
}

Type guard

function widthsAreStrings(d: unknown): d is Record<string, string> {
  return typeof d === 'object' && d !== null && !Array.isArray(d) && Object.values(d).every(v => typeof v === 'string')
}

Prevention

When it happens

Trigger: The widths object contains a numeric value (e.g. 200 instead of "200"), null, or a nested object for one of the columns. Often a frontend serialization oversight where a number was not coerced to string.

Common situations: Client storing widths as numbers in state and forwarding them unconverted; a column whose width was never set and serializes as null.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/1783409bfd43d2f6. Report an issue: GitHub.