can1357/oh-my-pi · error · RuntimeError
Output #{output_id} is not valid JSON: #{e.message}
Error message
Output #{output_id} is not valid JSON: #{e.message} What it means
`output` with a `query` argument assumes the artifact file contains JSON, because the query is applied to the parsed document. When `JSON.parse(raw)` fails on the artifact's contents, the prelude re-raises with the output ID and the parser's message so the offending artifact can be identified.
Source
Thrown at packages/coding-agent/src/eval/rb/prelude.rb:208
ids.each do |output_id|
path = File.join(artifacts_dir, "#{output_id}.md")
unless File.exist?(path)
not_found << output_id
next
end
raw = File.read(path, encoding: Encoding::UTF_8)
raw_lines = raw.split("\n", -1)
total_lines = raw_lines.length
selected = raw
range_info = nil
if query
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 }View on GitHub (pinned to 9690622007)
Solutions
- Read the artifact without `query` first (`output('<id>')`) to inspect its actual content and confirm whether it is JSON.
- Fix the upstream step that produced the artifact so it writes valid JSON (e.g. pipe through `jq .` or serialize with JSON.generate).
- Use `offset`/`limit` line paging or `format:` instead of `query` for non-JSON artifacts.
Example fix
// before
data = output('result_1', query: '$.items') # artifact is plain text
// after
raw = output('result_1') # inspect first
parsed = JSON.parse(raw) rescue nil
data = parsed ? output('result_1', query: '$.items') : raw Defensive patterns
Strategy: validation
Validate before calling
raw = output(id) begin JSON.parse(raw) rescue JSON::ParserError # artifact is not JSON — do not use query: end
Type guard
def valid_json?(str) JSON.parse(str) true rescue JSON::ParserError false end
Try / catch
begin
result = output(id, query: q)
rescue RuntimeError => e
raise unless e.message.include?('not valid JSON')
result = output(id) # raw fallback
end Prevention
- Only use `query:` on artifacts produced by steps that guarantee JSON output.
- Validate artifact content with a JSON.parse smoke check before querying.
- Ensure producer steps serialize completely (no truncation) before the consumer reads.
When it happens
Trigger: Calling `output('<id>', query: ...)` where `<id>.md` in PI_ARTIFACTS_DIR (or the session-derived artifacts dir) contains plain text, Markdown, truncated JSON, or a trailing newline-joined log rather than a complete JSON document.
Common situations: Pointing query at an artifact that was written as raw command output or Markdown notes; an earlier run crashed mid-write leaving truncated JSON; assuming all artifacts are JSON when only some are.
Related errors
- No session - output artifacts unavailable
- No artifacts directory found: #{artifacts_dir}
- query cannot be combined with offset/limit
- Offset #{start_line} is beyond end of output (#{total_lines}
- tool bridge is unavailable in this kernel
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/f21b1ee0f8e51d75.
Report an issue: GitHub.