siyuan-note/siyuan · warning

invalid card cover image

Error message

invalid card cover image

What it means

Returned by setAttrViewCardCoverPosition (attribute_view.go:1919) when data.Position is non-nil but its Image field is empty or exceeds 32*1024 bytes (32 KiB). The Image field holds the cover image payload (a data URL / base64 used as a cache key for the position). The guard runs only when a position object is present; sending position = null is valid and clears the stored position.

Source

Thrown at kernel/model/attribute_view.go:1919

	}
	return
}

func setAttrViewCardCoverPosition(operation *Operation) (err error) {
	dataJSON, err := json.Marshal(operation.Data)
	if nil != err {
		return
	}
	var data setAttrViewCardCoverPositionData
	if err = json.Unmarshal(dataJSON, &data); nil != err {
		return
	}
	if !av.IsValidCardCoverSource(data.Source) {
		return fmt.Errorf("invalid card cover source [%s]", data.Source)
	}
	if nil != data.Position {
		if "" == data.Position.Image || 32*1024 < len(data.Position.Image) {
			return errors.New("invalid card cover image")
		}
		if math.IsNaN(data.Position.X) || math.IsInf(data.Position.X, 0) ||
			math.IsNaN(data.Position.Y) || math.IsInf(data.Position.Y, 0) ||
			data.Position.X < 0 || 100 < data.Position.X || data.Position.Y < 0 || 100 < data.Position.Y {
			return fmt.Errorf("invalid card cover position [%v, %v]", data.Position.X, data.Position.Y)
		}
	}

	attrView, err := av.ParseAttributeView(operation.AvID)
	if nil != err {
		return
	}
	if nil == attrView.GetBlockValue(operation.RowID) {
		return fmt.Errorf("attribute view item [%s] not found", operation.RowID)
	}
	view, err := getAttrViewViewByBlockID(attrView, operation.BlockID)
	if nil != err {
		return

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Send a short, stable image identifier (e.g. the asset URL/hash, not the full base64) under 32 KiB in position.image, or omit the position object to clear it.
  2. If you only want to set X/Y focus, pass the same image reference the renderer uses as the cache key, trimmed to well under 32 KiB.
  3. Send position = null when clearing the focal point rather than an object with an empty Image.

Example fix

// before: embedding the full base64 cover
transaction({ op: "setAttrViewCardCoverPosition", data: { source, position: { image: fullBase64, x, y } } })

// after: use a short cache key (the asset URL/hash)
transaction({ op: "setAttrViewCardCoverPosition", data: { source, position: { image: coverURL, x, y } } })
Defensive patterns

Strategy: validation

Validate before calling

// image must be a short cache key, not full base64; cap at 32 KiB.
if (position && (position.image === '' || position.image.length > 32 * 1024)) {
  throw new Error('invalid card cover image')
}

Type guard

function isValidCoverImage(position: unknown): boolean {
  if (position == null) return true // null clears the position
  if (typeof position !== 'object') return false
  const img = (position as any).image
  return typeof img === 'string' && img.length > 0 && img.length <= 32 * 1024
}

Prevention

When it happens

Trigger: Frontend sends a non-null position with an empty Image string, or with an Image string longer than 32 KiB (a large base64-encoded cover). The 32 KiB cap exists because the image is stored as a position-cache key in av.CardCoverPositions, not as the actual asset.

Common situations: Plugin embeds the full image base64 instead of a short cache key; UI sends an empty Image because the cover URL was used instead; cover image changed and the stale payload was not refreshed, blowing past the limit.

Related errors


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