oraios/serena · error · ValueError

Body end position is not defined for {self}

Error message

Body end position is not defined for {self}

What it means

Strict counterpart of the body-start accessor: get_body_end_position_or_raise raises when the symbol's body end position is None. Used by replace_body, insert_after_symbol and delete_symbol, which need a valid end to compute the edit range.

Source

Thrown at src/serena/symbol.py:120

    def name(self) -> str:
        pass

    def get_body_start_position_or_raise(self) -> PositionInFile:
        """
        Get the start position of the symbol body, raising an error if it is not defined.
        """
        pos = self.get_body_start_position()
        if pos is None:
            raise ValueError(f"Body start position is not defined for {self}")
        return pos

    def get_body_end_position_or_raise(self) -> PositionInFile:
        """
        Get the end position of the symbol body, raising an error if it is not defined.
        """
        pos = self.get_body_end_position()
        if pos is None:
            raise ValueError(f"Body end position is not defined for {self}")
        return pos

    @abstractmethod
    def is_neighbouring_definition_separated_by_empty_line(self) -> bool:
        """
        :return: whether a symbol definition of this symbol's kind is usually separated from the
            previous/next definition by at least one empty line.
        """


class NamePathComponent:
    def __init__(self, name: str, overload_idx: int | None = None) -> None:
        self.name = name
        self.overload_idx = overload_idx

    def __repr__(self) -> str:
        if self.overload_idx is not None:
            return f"{self.name}[{self.overload_idx}]"

View on GitHub (pinned to 7fcbca7e62)

Solutions

  1. Verify get_body_end_position() is not None before the mutating call
  2. Target only complete definitions (functions, classes, methods)
  3. Re-resolve the symbol after prior edits since stale positions can invalidate ranges

Example fix

// before
symbol.insert_after_symbol(new_code)
// after
if symbol.get_body_end_position() is not None:
    symbol.insert_after_symbol(new_code)
Defensive patterns

Strategy: type-guard

Validate before calling

if symbol.get_body_end_position() is None:
    raise SkipSymbol(f"no end position: {symbol.get_name_path()}")

Type guard

def has_editable_body(symbol) -> bool:
    return symbol.get_body_start_position() is not None and symbol.get_body_end_position() is not None

Try / catch

try:
    symbol.insert_after_symbol(new_code)
except ValueError as e:
    if "Body end position is not defined" in str(e):
        handle_bodyless_symbol(symbol)
    else:
        raise

Prevention

When it happens

Trigger: insert_after_symbol/replace_body/delete_symbol on symbols whose end position is unavailable (body-less constructs, incomplete language-server ranges).

Common situations: Same as body-start: non-definitional symbols, languages with partial range support, stale symbol handles after file edits.

Related errors


AI-assisted analysis of oraios/serena@7fcbca7e62 (2026-08-29). Data as JSON: /api/errors/73fd4d572fa5314d. Report an issue: GitHub.