@@ -421,6 +421,151 @@ def handler(payload):
421421 assert response .headers .get ("x-custom-mw" ) == "mw"
422422
423423
424+ class TestCustomStatusCodes :
425+ """Test that entrypoint handlers can return custom HTTP status codes (#284)."""
426+
427+ def test_http_exception_returns_custom_status (self ):
428+ """Test raising HTTPException returns its status code instead of 500."""
429+ from starlette .exceptions import HTTPException
430+
431+ app = BedrockAgentCoreApp ()
432+
433+ @app .entrypoint
434+ def handler (payload ):
435+ raise HTTPException (status_code = 400 , detail = "Prompt missing" )
436+
437+ client = TestClient (app )
438+ response = client .post ("/invocations" , json = {})
439+
440+ assert response .status_code == 400
441+ assert response .json () == {"error" : "Prompt missing" }
442+
443+ def test_http_exception_422 (self ):
444+ """Test HTTPException with 422 status code."""
445+ from starlette .exceptions import HTTPException
446+
447+ app = BedrockAgentCoreApp ()
448+
449+ @app .entrypoint
450+ def handler (payload ):
451+ raise HTTPException (status_code = 422 , detail = "Validation failed" )
452+
453+ client = TestClient (app )
454+ response = client .post ("/invocations" , json = {})
455+
456+ assert response .status_code == 422
457+ assert response .json () == {"error" : "Validation failed" }
458+
459+ def test_http_exception_500_still_returns_500 (self ):
460+ """Test HTTPException with 500 is not swallowed."""
461+ from starlette .exceptions import HTTPException
462+
463+ app = BedrockAgentCoreApp ()
464+
465+ @app .entrypoint
466+ def handler (payload ):
467+ raise HTTPException (status_code = 500 , detail = "Intentional server error" )
468+
469+ client = TestClient (app )
470+ response = client .post ("/invocations" , json = {})
471+
472+ assert response .status_code == 500
473+ assert response .json () == {"error" : "Intentional server error" }
474+
475+ def test_return_response_passthrough (self ):
476+ """Test returning a Response object passes it through without wrapping."""
477+ from starlette .responses import JSONResponse
478+
479+ app = BedrockAgentCoreApp ()
480+
481+ @app .entrypoint
482+ def handler (payload ):
483+ return JSONResponse ({"error" : "not found" }, status_code = 404 )
484+
485+ client = TestClient (app )
486+ response = client .post ("/invocations" , json = {})
487+
488+ assert response .status_code == 404
489+ assert response .json () == {"error" : "not found" }
490+
491+ def test_return_response_200_passthrough (self ):
492+ """Test returning a Response with 200 also passes through."""
493+ from starlette .responses import JSONResponse
494+
495+ app = BedrockAgentCoreApp ()
496+
497+ @app .entrypoint
498+ def handler (payload ):
499+ return JSONResponse ({"custom" : True }, status_code = 200 , headers = {"x-custom" : "yes" })
500+
501+ client = TestClient (app )
502+ response = client .post ("/invocations" , json = {})
503+
504+ assert response .status_code == 200
505+ assert response .json () == {"custom" : True }
506+ assert response .headers .get ("x-custom" ) == "yes"
507+
508+ def test_normal_dict_return_still_200 (self ):
509+ """Test that normal dict returns are unaffected."""
510+ app = BedrockAgentCoreApp ()
511+
512+ @app .entrypoint
513+ def handler (payload ):
514+ return {"message" : "ok" }
515+
516+ client = TestClient (app )
517+ response = client .post ("/invocations" , json = {})
518+
519+ assert response .status_code == 200
520+ assert response .json () == {"message" : "ok" }
521+
522+ def test_generic_exception_still_500 (self ):
523+ """Test that non-HTTP exceptions still return 500."""
524+ app = BedrockAgentCoreApp ()
525+
526+ @app .entrypoint
527+ def handler (payload ):
528+ raise ValueError ("something broke" )
529+
530+ client = TestClient (app )
531+ response = client .post ("/invocations" , json = {})
532+
533+ assert response .status_code == 500
534+ assert response .json () == {"error" : "something broke" }
535+
536+ def test_async_handler_http_exception (self ):
537+ """Test HTTPException works from async handlers too."""
538+ from starlette .exceptions import HTTPException
539+
540+ app = BedrockAgentCoreApp ()
541+
542+ @app .entrypoint
543+ async def handler (payload ):
544+ raise HTTPException (status_code = 400 , detail = "Bad request" )
545+
546+ client = TestClient (app )
547+ response = client .post ("/invocations" , json = {})
548+
549+ assert response .status_code == 400
550+ assert response .json () == {"error" : "Bad request" }
551+
552+ def test_async_handler_response_passthrough (self ):
553+ """Test returning Response from async handler."""
554+ from starlette .responses import JSONResponse
555+
556+ app = BedrockAgentCoreApp ()
557+
558+ @app .entrypoint
559+ async def handler (payload ):
560+ return JSONResponse ({"error" : "gone" }, status_code = 410 )
561+
562+ client = TestClient (app )
563+ response = client .post ("/invocations" , json = {})
564+
565+ assert response .status_code == 410
566+ assert response .json () == {"error" : "gone" }
567+
568+
424569class TestConcurrentInvocations :
425570 """Test concurrent invocation handling simplified without limits."""
426571
0 commit comments