{"record":{"id":"f21b1ee0f8e51d75","repo":"can1357/oh-my-pi","slug":"output-output-id-is-not-valid-json-e-message","errorCode":null,"errorMessage":"Output #{output_id} is not valid JSON: #{e.message}","messagePattern":"Output #(.+?) is not valid JSON: #(.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/eval/rb/prelude.rb","lineNumber":208,"sourceCode":"    ids.each do |output_id|\n      path = File.join(artifacts_dir, \"#{output_id}.md\")\n      unless File.exist?(path)\n        not_found << output_id\n        next\n      end\n      raw = File.read(path, encoding: Encoding::UTF_8)\n      raw_lines = raw.split(\"\\n\", -1)\n      total_lines = raw_lines.length\n      selected = raw\n      range_info = nil\n\n      if query\n        json_value =\n          begin\n            JSON.parse(raw)\n          rescue JSON::ParserError => e\n            __omp_emit_status(\"output\", \"id\" => output_id, \"error\" => \"Not valid JSON: #{e.message}\")\n            raise \"Output #{output_id} is not valid JSON: #{e.message}\"\n          end\n        result_value = __omp_apply_query(json_value, query)\n        selected =\n          begin\n            result_value.nil? ? \"null\" : JSON.pretty_generate(result_value)\n          rescue StandardError\n            result_value.to_s\n          end\n      elsif !offset.nil? || !limit.nil?\n        start_line = [offset || 1, 1].max\n        if start_line > total_lines\n          __omp_emit_status(\"output\", \"id\" => output_id, \"error\" => \"Offset #{start_line} beyond end (#{total_lines} lines)\")\n          raise \"Offset #{start_line} is beyond end of output (#{total_lines} lines) for #{output_id}\"\n        end\n        effective_limit = limit || (total_lines - start_line + 1)\n        end_line = [total_lines, start_line + effective_limit - 1].min\n        selected = raw_lines[(start_line - 1)...end_line].join(\"\\n\")\n        range_info = { \"start_line\" => start_line, \"end_line\" => end_line, \"total_lines\" => total_lines }","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/eval/rb/prelude.rb#L190-L226","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ndata = output('result_1', query: '$.items')  # artifact is plain text\n// after\nraw = output('result_1')                      # inspect first\nparsed = JSON.parse(raw) rescue nil\ndata = parsed ? output('result_1', query: '$.items') : raw","handlingStrategy":"validation","validationCode":"raw = output(id)\nbegin\n  JSON.parse(raw)\nrescue JSON::ParserError\n  # artifact is not JSON — do not use query:\nend","typeGuard":"def valid_json?(str)\n  JSON.parse(str)\n  true\nrescue JSON::ParserError\n  false\nend","tryCatchPattern":"begin\n  result = output(id, query: q)\nrescue RuntimeError => e\n  raise unless e.message.include?('not valid JSON')\n  result = output(id) # raw fallback\nend","preventionTips":["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."],"tags":["ruby","json-parse","eval-prelude","artifacts"],"backgroundTag":"invalid-json","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}