NationalSecurityAgency/ghidra · error · ValueError

Schedule must be in form [snap]:[steps]. Got '{s}'

Error message

Schedule must be in form [snap]:[steps]. Got '{s}'

What it means

Raised by Schedule.parse() when the input string contains more than one colon, i.e. it cannot be split into a [snap] or [snap]:[steps] pair. The Schedule dataclass represents a position in a trace as a snapshot id plus an optional step count. Note that non-numeric parts like 'abc' hit int() first and raise a different ValueError, so this specific message only fires on inputs with 3+ colon-separated parts.

Source

Thrown at Ghidra/Debug/Debugger-rmi-trace/src/main/py/src/ghidratrace/client.py:157

    Until we have need more capable schedules here, we'll just keep it
    at this. TODO: We might need another flag to indicate the kind of
    steps here. It seems in Microsoft TTD, it's the number of
    instructions executed since the last event. However, in rr/gdb, it
    seems it's the number of branch instructions encountered.
    """
    snap: int
    steps: int = 0

    @staticmethod
    def parse(s: str) -> 'Schedule':
        parts = s.split(':')
        if len(parts) == 1:
            return Schedule(int(parts[0]))
        elif len(parts) == 2:
            return Schedule(int(parts[0]), int(parts[1]))
        else:
            raise ValueError(
                f"Schedule must be in form [snap]:[steps]. Got '{s}'")

    def __str__(self) -> str:
        if self.steps == 0:
            return f"{self.snap}"
        return f"{self.snap}:{self.steps}"


@dataclass(frozen=True)
class DetachedObject:
    id: int
    path: str


@dataclass(frozen=True)
class TraceObject:
    """A proxy for a TraceObject."""
    trace: 'Trace'

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Strip/normalize the input to a single optional colon before calling parse: pre-split on ':' and reject early if len != 1 or 2.
  2. Validate with a regex like ^\d+(:\d+)?$ before calling parse so the error never reaches the library.
  3. Wrap parse() in try/except ValueError and report the offending value back to the user rather than letting it surface raw.

Example fix

// before
sched = Schedule.parse(user_input)
// after
import re
if not re.fullmatch(r'\d+(:\d+)?', user_input.strip()):
    raise ValueError(f'Expected [snap]:[steps], got {user_input!r}')
sched = Schedule.parse(user_input.strip())
Defensive patterns

Strategy: validation

Validate before calling

import re
assert isinstance(s, str)
if not re.fullmatch(r'\d+(:\d+)?', s.strip()):
    raise ValueError(f'Bad schedule {s!r}; expected [snap]:[steps]')

Type guard

def is_schedule_str(s) -> bool:
    import re
    return isinstance(s, str) and bool(re.fullmatch(r'\d+(:\d+)?', s.strip()))

Try / catch

try:
    sched = Schedule.parse(raw)
except ValueError:
    # report to user, fall back, or re-raise with context
    raise

Prevention

When it happens

Trigger: Calling Schedule.parse('1:2:3'), Schedule.parse('a:b:c:d'), or passing a snapshot path/range string that contains multiple colons. Any caller that forwards unvalidated user or file-derived text into parse() reaches this branch.

Common situations: Pasting a schedule from a UI that formats it as 'snap:steps:extra', reading a malformed config/script argument, or a debugger front-end that passes a full timestamp or address-range string where a bare schedule is expected.

Related errors


AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14). Data as JSON: /api/errors/978688027b9b3faa. Report an issue: GitHub.