"""Cheap deliberate error tests for the Anthropic API (nothing is billed: every request is rejected). Verified live 2026-09-18.""" from __future__ import annotations import pytest MSG = "/v1/messages" def _body(model, **kw): b = {"model": model, "max_tokens": 8, "messages": [{"role": "user", "content": "Reply with OK."}]} b.update(kw) return b def _assert_error(st, body, hdrs, status, etype): assert st == status, body assert body["type"] == "error" and body["error"]["type"] == etype assert body["request_id"].startswith("req_") lower = {k.lower(): v for k, v in hdrs.items()} assert lower["request-id"] == body["request_id"] assert lower.get("x-should-retry") == "false" # LIVE_DISCOVERED header on every 4xx return body["error"]["message"] def test_404_unknown_model(anthropic, models): st, body, hdrs = anthropic("POST", MSG, _body("claude-does-not-exist"), note="test_errors 404 model") assert _assert_error(st, body, hdrs, 404, "not_found_error").startswith("model:") def test_400_missing_max_tokens(anthropic, models): b = _body(models["anthropic"]); del b["max_tokens"] st, body, hdrs = anthropic("POST", MSG, b, note="test_errors 400 max_tokens") assert "max_tokens" in _assert_error(st, body, hdrs, 400, "invalid_request_error") def test_400_max_tokens_over_model_max(anthropic, models): st, body, hdrs = anthropic("POST", MSG, _body(models["anthropic"], max_tokens=1_000_000), note="test_errors 400 max_tokens too large") assert "64000" in _assert_error(st, body, hdrs, 400, "invalid_request_error") @pytest.mark.parametrize("field,value,fragment", [("temperature", 5, "range"), ("top_k", "abc", "integer"), ("messages", [], "at least one")]) def test_400_validation(anthropic, models, field, value, fragment): st, body, hdrs = anthropic("POST", MSG, _body(models["anthropic"], **{field: value}), note=f"test_errors 400 {field}") assert fragment in _assert_error(st, body, hdrs, 400, "invalid_request_error") def test_400_temperature_and_top_p_together(anthropic, models): st, body, hdrs = anthropic("POST", MSG, _body(models["anthropic"], temperature=0.5, top_p=0.9), note="test_errors 400 temperature+top_p") assert "cannot both be specified" in _assert_error(st, body, hdrs, 400, "invalid_request_error") def test_401_bad_key(anthropic, models): st, body, hdrs = anthropic("POST", MSG, _body(models["anthropic"]), extra_headers={"x-api-key": "invalid-key-placeholder"}, note="test_errors 401") assert st == 401 and body["error"]["type"] == "authentication_error" def test_400_invalid_version_header(anthropic, models): st, body, hdrs = anthropic("POST", MSG, _body(models["anthropic"]), extra_headers={"anthropic-version": "2020-01-01"}, note="test_errors version") assert "anthropic-version" in _assert_error(st, body, hdrs, 400, "invalid_request_error") def test_400_unknown_beta_header(anthropic, models): st, body, hdrs = anthropic("POST", MSG, _body(models["anthropic"]), beta="does-not-exist-2099-01-01", note="test_errors beta") assert "anthropic-beta" in _assert_error(st, body, hdrs, 400, "invalid_request_error") def test_400_prefill_rejected_on_claude_5(anthropic): st, body, hdrs = anthropic("POST", MSG, {"model": "claude-sonnet-5", "max_tokens": 8, "messages": [ {"role": "user", "content": "Say OK."}, {"role": "assistant", "content": "The word is:"}]}, note="test_errors prefill sonnet-5") assert "prefill" in _assert_error(st, body, hdrs, 400, "invalid_request_error") def test_400_legacy_complete_deprecated(anthropic): st, body, hdrs = anthropic("POST", "/v1/complete", {"model": "claude-2.1", "max_tokens_to_sample": 8, "prompt": "\n\nHuman: hi\n\nAssistant:"}, note="test_errors /v1/complete") assert "deprecated" in _assert_error(st, body, hdrs, 400, "invalid_request_error")