What happens
Linting a module via POST /listings/lint only works if payload.module_json is a JSON STRING. Send the parsed manifest (an object) and the response is errorCount:1, code module.manifest_unparseable, message "module.json is missing or not valid JSON" — even though the object is valid JSON. The error blames the manifest content when the real issue is the wire type.
Repro
Same manifest, two calls to https://railcall-marketplace-lggm.onrender.com/listings/lint :
- payload.module_json = {parsed dict} -> module.manifest_unparseable
- payload.module_json = json.dumps(dict) (string) -> ok:true, 0/0
Only difference is the wire type of module_json.
Why it is confusing
The Publish rejections doc shows payload as "payload": <spec/module> with module.json rendered as a JSON object, so callers nest the parsed manifest. The CLI happens to hold the manifest as text and sends a string, which is why the CLI path works and hand-rolled lint calls do not.
Fix (either)
- Accept module_json as an object too (parse-or-passthrough), or
- Return module.manifest_wrong_type ("module_json must be a JSON string") instead of manifest_unparseable, so the message matches the cause.
Impact
The docs explicitly recommend linting before publish; the first hand-rolled call hits a wrong-cause error and sends people debugging their manifest instead of their request shape.