"""Force the real ECDS rejection reason: submit, wait for terminal state, then fetch the job's /results endpoint (a 4xx whose JSON carries title/traceback/ detail). Read-only; captures the exact error the cdsapi wrapper hides.""" import json import os import time import traceback import datapi ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ECDS_URL = "https://ecds.ecmwf.int/api" DATASET = "s2s-reforecasts" req = json.load(open(os.path.join(ROOT, "config", "ecds_minimal_request.json"))) req.pop("_comment", None) def _get_key(): k = os.environ.get("CDSAPI_KEY") if k: return k rc = os.environ.get("CDSAPI_RC", os.path.expanduser("~/.cdsapirc")) if os.path.exists(rc): for line in open(rc): if line.strip().startswith("key"): return line.split(":", 1)[1].strip() return None out = {"request": req} c = datapi.ApiClient(url=ECDS_URL, key=_get_key()) remote = c.submit(DATASET, req) out["request_id"] = remote.request_id print("submitted", remote.request_id, flush=True) term = {"successful", "failed", "rejected", "dismissed", "deleted"} st = None for _ in range(60): st = remote.status if st in term: break time.sleep(3) out["final_status"] = st print("final:", st, flush=True) # force the results endpoint to reveal the error document try: results = remote._make_results(wait=False) out["results_json"] = getattr(results, "json", None) out["reason"] = "results returned OK (unexpected for rejected)" except Exception as e: out["reason"] = f"{type(e).__name__}: {e}" out["reason_traceback"] = traceback.format_exc()[-2500:] with open(os.path.join(ROOT, "metadata", "ecds_reject_reason.json"), "w") as f: json.dump(out, f, indent=2, default=str) print("\n=== REASON ===", flush=True) print(out.get("reason"), flush=True)