The relation-set hook tool (_relation_set in src/jjx/_cmd_hook_tool.py) parses its stdin input exclusively as JSON (json.loads(content)). However, the format ops sends depends on the ops version: ops 2.x uses yaml.safe_dump(data) (YAML key-value format), while ops 3.x uses json.dumps(data) via the hookcmds module. Since jjx bind-mounts the charm project's own venv into the charm runner, the ops version is determined by the charm, not by jjx. Any charm using ops 2.x will fail on every relation-set call with invalid relation-set JSON: Expecting value: line 1 column 1, because the YAML input (e.g. flask_secret_key: abc123\n) is not valid JSON.
Parse the stdin content with a JSON-first, YAML-fallback strategy: try json.loads, and if that raises JSONDecodeError, fall back to yaml.safe_load. Both formats produce a dict, so the rest of the handler is unchanged. This is backward-compatible with ops 3.x (JSON) and fixes ops 2.x (YAML).
The
relation-sethook tool (_relation_setinsrc/jjx/_cmd_hook_tool.py) parses its stdin input exclusively as JSON (json.loads(content)). However, the format ops sends depends on the ops version: ops 2.x usesyaml.safe_dump(data)(YAML key-value format), while ops 3.x usesjson.dumps(data)via thehookcmdsmodule. Since jjx bind-mounts the charm project's own venv into the charm runner, the ops version is determined by the charm, not by jjx. Any charm using ops 2.x will fail on everyrelation-setcall withinvalid relation-set JSON: Expecting value: line 1 column 1, because the YAML input (e.g.flask_secret_key: abc123\n) is not valid JSON.Parse the stdin content with a JSON-first, YAML-fallback strategy: try
json.loads, and if that raisesJSONDecodeError, fall back toyaml.safe_load. Both formats produce a dict, so the rest of the handler is unchanged. This is backward-compatible with ops 3.x (JSON) and fixes ops 2.x (YAML).