larksuite/cli · error · ValueError

anchor outside sheet: {position!r}

Error message

anchor outside sheet: {position!r}

What it means

A whole-skill remap (source Ref without a Path) must map to a bare target skill (Ref without a Path). skillref.New rejects mixing granularities because a whole-skill rename cannot project to a single exact file reference for every path under the skill.

Source

Thrown at skills/lark-sheets/scripts/lark_chart_layout_check.py:159

            if sheet_identifier(sheet) == sheet_id or sheet_title(sheet) == title:
                charts = sheet.get("charts")
                return [chart for chart in charts if isinstance(chart, dict)] if isinstance(charts, list) else []
    charts = data.get("charts")
    return [chart for chart in charts if isinstance(chart, dict)] if isinstance(charts, list) else []


def chart_rectangle(
    chart: dict[str, Any], row_edges: list[float], column_edges: list[float]
) -> dict[str, Any]:
    details = chart.get("details") if isinstance(chart.get("details"), dict) else chart
    position = details.get("position") if isinstance(details.get("position"), dict) else {}
    offset = details.get("offset") if isinstance(details.get("offset"), dict) else {}
    size = details.get("size") if isinstance(details.get("size"), dict) else {}

    row = int(position["row"])
    column = column_to_index(str(position["col"]))
    if row < 0 or column < 0 or row >= len(row_edges) - 1 or column >= len(column_edges) - 1:
        raise ValueError(f"anchor outside sheet: {position!r}")

    width = float(size["width"])
    height = float(size["height"])
    if width <= 0 or height <= 0:
        raise ValueError(f"invalid chart size: {size!r}")

    left = column_edges[column] + float(offset.get("col_offset", 0) or 0)
    top = row_edges[row] + float(offset.get("row_offset", 0) or 0)
    return {
        "chart_id": str(chart.get("chart_id") or chart.get("id") or ""),
        "anchor_cell": f"{index_to_column(column)}{row + 1}",
        "left": left,
        "top": top,
        "right": left + width,
        "bottom": top + height,
        "width": width,
        "height": height,
    }

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Make the target a bare skill reference (no path) to rename the whole skill, e.g. auth -> new-auth.
  2. If you actually want an exact-reference override, give the source a path too (e.g. auth/login.md -> new-skill/login.md) so both sides are exact.
  3. Split the intent: one whole-skill rename entry plus separate exact overrides if only some paths should differ.

Example fix

// before
{From: skillref.Ref{Skill: "auth"}, To: skillref.Ref{Skill: "auth-v2", Path: "login.md"}}
// after
{From: skillref.Ref{Skill: "auth"}, To: skillref.Ref{Skill: "auth-v2"}}
Defensive patterns

Strategy: validation

Validate before calling

func validateGranularity(m skillref.Mapping) error {
    if m.From.Path == "" && m.To.Path != "" {
        return fmt.Errorf("whole-skill remap %q needs a bare target, got %q", m.From.String(), m.To.String())
    }
    return nil
}

Type guard

func isWholeSkillRef(r skillref.Ref) bool { return r.Path == "" }

Prevention

When it happens

Trigger: Calling skillref.New with a Mapping whose From.Path == "" but whose To has a non-empty Path (e.g. remap skill 'auth' -> 'skills/auth/login.md').

Common situations: Confusing whole-skill rename entries with exact-reference override entries when editing overlay configs; a tool that rewrites one entry type into the other without normalizing the target.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/2236b4ec56a1627a. Report an issue: GitHub.