siyuan-note/siyuan · error

invalid card cover position [%v, %v]

Error message

invalid card cover position [%v, %v]

What it means

Thrown by setAttrViewCardCoverPosition when a card cover position's X or Y coordinate is NaN, +/-Inf, or outside the allowed [0, 100] range. SiYuan stores cover-image focal position as percentages, so any value below 0 or above 100 (or non-finite) is rejected before the AttributeView is saved. The image itself is validated separately (non-empty and <= 32KB) just above this check.

Source

Thrown at kernel/model/attribute_view.go:1924

	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
	}
	if av.LayoutTypeGallery != view.LayoutType && av.LayoutTypeKanban != view.LayoutType {
		return av.ErrWrongLayoutType
	}
	var source string

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Clamp both X and Y to the inclusive range [0, 100] on the client before sending the operation, and ensure the values are finite numbers.
  2. If calling the kernel API directly, validate with math.IsNaN/math.IsInf and bound-check before constructing the operation Data.
  3. Update the frontend to a version matching the kernel so the cover-position contract (percentage 0-100) is honored.

Example fix

// before
op.Data = { source: "content", position: { image: b64, x: rawPxX, y: rawPxY } }
// after
const clamp = v => Math.max(0, Math.min(100, (Number.isFinite(v) ? v : 50)))
op.Data = { source: "content", position: { image: b64, x: clamp(xPct), y: clamp(yPct) } }
Defensive patterns

Strategy: validation

Validate before calling

function isValidCoverPosition(p) {
  if (p == null) return true // null position clears the entry
  return Number.isFinite(p.x) && Number.isFinite(p.y) && p.x >= 0 && p.x <= 100 && p.y >= 0 && p.y <= 100 && typeof p.image === 'string' && p.image.length > 0 && p.image.length <= 32 * 1024
}
if (!isValidCoverPosition(op.data.position)) { /* do not send */ }

Type guard

type CardCoverPos = { image: string; x: number; y: number }
function isCardCoverPos(v: unknown): v is CardCoverPos | null {
  if (v == null) return true
  if (typeof v !== 'object' || v === null) return false
  const p = v as Record<string, unknown>
  return typeof p.image === 'string' && typeof p.x === 'number' && typeof p.y === 'number' && Number.isFinite(p.x) && Number.isFinite(p.y)
}

Prevention

When it happens

Trigger: A transaction operation of action setAttrViewCardCoverPosition whose Data.Position is non-nil with X or Y outside 0..100, or a payload produced by a buggy/older frontend that sends pixel offsets instead of percentages. NaN/Inf reach the check when the JSON payload contains null cast to float64 or a corrupted numeric field.

Common situations: Frontend version skew where an older client sends raw pixel coordinates; a manually crafted API call; a rounding/clamping bug in the drag handler that occasionally yields -0.0001 or 100.0001.

Related errors


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