can1357/oh-my-pi · error

Offset #{start_line} is beyond end of output (#{total_lines}

Error message

Offset #{start_line} is beyond end of output (#{total_lines} lines) for #{output_id}

What it means

When `output` is called with `offset` (and/or `limit`), the artifact is paged by lines. The offset is clamped to a minimum of 1, but if the requested start line exceeds the artifact's total line count there is nothing to return, so the prelude raises with the requested start line, the total lines, and the output ID.

Source

Thrown at packages/coding-agent/src/eval/rb/prelude.rb:221

        json_value =
          begin
            JSON.parse(raw)
          rescue JSON::ParserError => e
            __omp_emit_status("output", "id" => output_id, "error" => "Not valid JSON: #{e.message}")
            raise "Output #{output_id} is not valid JSON: #{e.message}"
          end
        result_value = __omp_apply_query(json_value, query)
        selected =
          begin
            result_value.nil? ? "null" : JSON.pretty_generate(result_value)
          rescue StandardError
            result_value.to_s
          end
      elsif !offset.nil? || !limit.nil?
        start_line = [offset || 1, 1].max
        if start_line > total_lines
          __omp_emit_status("output", "id" => output_id, "error" => "Offset #{start_line} beyond end (#{total_lines} lines)")
          raise "Offset #{start_line} is beyond end of output (#{total_lines} lines) for #{output_id}"
        end
        effective_limit = limit || (total_lines - start_line + 1)
        end_line = [total_lines, start_line + effective_limit - 1].min
        selected = raw_lines[(start_line - 1)...end_line].join("\n")
        range_info = { "start_line" => start_line, "end_line" => end_line, "total_lines" => total_lines }
      end

      selected = selected.gsub(/\e\[[0-9;]*m/, "") if format == "stripped"

      if format == "json"
        entry = {
          "id" => output_id,
          "path" => path,
          "line_count" => query ? selected.split("\n").length : total_lines,
          "char_count" => query ? selected.length : raw.length,
          "content" => selected,
        }
        entry["range"] = range_info if range_info

View on GitHub (pinned to 9690622007)

Solutions

  1. Use a smaller offset within the artifact's actual length (the error message states total_lines).
  2. Detect the end of paging: catch the error and stop paginating, or first read without offset and count lines.
  3. If you need to know the size up front, fetch `format: 'json'` and use the returned `line_count` before computing the next offset.

Example fix

// before
chunk = output('log', offset: 500, limit: 100)  # file only has 120 lines
// after
info = output('log', format: 'json')
chunk = info['line_count'] >= 500 ? output('log', offset: 500, limit: 100) : nil
Defensive patterns

Strategy: validation

Validate before calling

info = output(id, format: 'json')
total = info['line_count']
raise RangeError, 'offset past end' if offset > total

Try / catch

begin
  chunk = output(id, offset: next_offset, limit: page_size)
rescue RuntimeError => e
  raise unless e.message =~ /beyond end of output/
  chunk = nil # reached end of artifact
end

Prevention

When it happens

Trigger: Calling `output('<id>', offset: N)` (or `offset: N, limit: M`) where the `<id>.md` artifact has fewer than N lines — e.g. a 3-line log fetched with `offset: 10`, or a stale cached line count reused after the artifact was regenerated shorter.

Common situations: Paging through an artifact with a fixed page size and running past the end; re-running a paging script after the artifact shrank; off-by-one assumptions that offset is zero-based (clamped to 1 here).

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/0e3f914d8baeae16. Report an issue: GitHub.