Code Smell 302 - Misleading Status Codes

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • MyrinNew
    Senior Member
    • Feb 2024
    • 5168

    #1

    Code Smell 302 - Misleading Status Codes

    When your API says "Everything is fine!" but returns errors


    TL;DR: Returning a successful HTTP status when the actual result contains an error confuses the API consumers.


    Problems πŸ˜”

    • Status code confusion
    • Debugging difficulty
    • Client error handling
    • API contract violation
    • Human text parsing instead of code checking
    • Inconsistent behavior
    • The Least surprise principle violation


    Solutions πŸ˜ƒ

    1. Match status to content
    2. Use proper error codes
    3. Follow HTTP standards
    4. Implement consistent responses
    5. Test status codes
    6. Separate metadata from payload
    7. Avoid mixing success and errors
    8. Define a clear contract


    Context πŸ’¬

    You build an API that processes requests successfully at the HTTP transport level but encounters application-level errors.


    Instead of returning appropriate HTTP error status codes such as 400 (Bad Request) or 500 (Internal Server Error), you return 200 OK with error information in the response body.


    This creates a disconnect between what the HTTP status indicates and what happened, making it harder for clients to handle errors properly and for monitoring systems to detect issues.


    Sample Code πŸ“–

    Wrong ❌





    use axum::{
    http::StatusCode,
    response::Json,
    routing:ost,
    Router,
    };
    use serde_json::{json, Value};

    async fn process_payment(
    Json(payload): JsonValue>
    ) -> (StatusCode, JsonValue>) {
    let amount = payload.get("amount")
    .and_then(|v| v.as_f64());

    if amount.is_none() || amount.unwrap() 0.0 {
    return (
    StatusCode::OK, // Wrong: returning 200 for error
    Json(json!({"error": true, "message": "Invalid amount"}))
    );
    }

    if amount.unwrap() > 10000.0 {
    return (
    StatusCode::OK, // Wrong: returning 200 for error
    Json(json!({"error": true, "message": "Amount too large"}))
    );
    }

    // Simulate processing error
    if let Some(card) = payload.get("card_number") {
    if card.as_str().unwrap_or("").len() 16 {
    return (
    StatusCode::OK, // Wrong: returning 200 for error
    Json(json!({"error": true, "message": "Invalid card"}))
    );
    }
    }

    (
    StatusCode::OK, // THIS the only real 200 Status
    Json(json!({"success": true, "transaction_id": "12345"}))
    )
    }

    pub fn create_router() -> Router {
    Router::new().route("/payment", post(process_payment))
    }





    Right πŸ‘‰






    use axum::{
    http::StatusCode,
    response::Json,
    routing:ost,
    Router,
    };
    use serde_json::{json, Value};

    async fn process_payment(
    Json(payload): JsonValue>
    ) -> (StatusCode, JsonValue>) {
    let amount = payload.get("amount")
    .and_then(|v| v.as_f64());

    if amount.is_none() || amount.unwrap() 0.0 {
    return (
    StatusCode::BAD_REQUEST, // Correct: 400 for bad input
    Json(json!({"error": "Invalid amount provided"}))
    );
    }

    if amount.unwrap() > 10000.0 {
    return (
    StatusCode::UNPROCESSABLE_ENTITY,
    // Correct: 422 for business rule
    Json(json!({"error": "Amount exceeds transaction limit"}))
    );
    }

    // Validate card number
    if let Some(card) = payload.get("card_number") {
    if card.as_str().unwrap_or("").len() 16 {
    return (
    StatusCode::BAD_REQUEST,
    // Correct: 400 for validation error
    Json(json!({"error": "Invalid card number format"}))
    );
    }
    } else {
    return (
    StatusCode::BAD_REQUEST,
    // Correct: 400 for missing field
    Json(json!({"error": "Card number is required"}))
    );
    }

    // successful processing
    (
    StatusCode::OK,
    // Correct: 200 only for actual success
    Json(json!({"transaction_id": "12345", "status": "completed"}))
    )
    }

    pub fn create_router() -> Router {
    Router::new().route("/payment", post(process_payment))
    }





    Detection πŸ”

    [X] Semi-Automatic


    You can detect this smell when you see HTTP 200 responses that contain error fields, boolean error flags, or failure messages.


    Look for APIs that always return 200 regardless of the actual outcome.


    Check if your monitoring systems can properly detect failures and use mutation testing.


    if they can't distinguish between success and failure based on status codes, you likely have this problem.


    You can also watch client-side bugs caused by mismatched expectations.

    Exceptions πŸ›‘

    • Breaking Changes on existing API clients may require a breaking change to fix this smell.

    Tags 🏷️

    • Exceptions

    Level πŸ”‹

    [X] Intermediate

    Why the Bijection Is Important πŸ—ΊοΈ

    HTTP status codes exist to provide a standardized way to communicate the outcome of requests between systems.


    When you break this correspondence by returning success codes for failures, you create a mismatch between the HTTP protocol's semantic meaning and your application's actual behavior.


    This forces every client to parse response bodies to determine success or failure, making error handling inconsistent and unreliable.


    Monitoring systems, load balancers, and proxies rely on status codes to make routing and health decisions - misleading codes can cause these systems to make incorrect assumptions about your API's health.


    Coupling your decisions to an incorrect status code will break the MAPPER.


    Modeling a one-to-one relationship between the HTTP status code and the actual business result ensures clarity and predictability. When a 200 OK returns an internal error, the client assumes everything is fine, leading to silent failures and incorrect behaviors downstream.


    By maintaining this bijection , we ensure that developers and systems interacting with the API can trust the response without additional checks.

    AI Generation πŸ€–

    AI code generators often create this smell when developers ask for "simple API examples" without specifying proper error handling.


    The generators tend to focus on the happy path and return 200 for all responses to avoid complexity.


    When you prompt AI to create REST APIs, you must explicitly request proper HTTP status code handling and verify the standards by yourself.

    AI Detection πŸ₯ƒ

    Many AI assistants can detect this mismatch.

    Try Them! πŸ› 

    Remember: AI Assistants make lots of mistakes


    Suggested Prompt: Correct bad HTTP codes behavior



    Conclusion 🏁

    HTTP status codes are an important part of API design that enable proper error handling, monitoring, and client behavior.


    When you return misleading status codes, you break the implicit contract that HTTP provides making your API harder to integrate with and maintain.


    Always ensure your status codes accurately reflect the actual outcome of the operation.

    Relations πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘¨











    Code Smell 270 - Boolean APIs

    Maxi Contieri ・ Sep 19 '24

    #beginners
    #programming
    #tutorial
    #api




















    Code Smell 272 - API Chain

    Maxi Contieri ・ Sep 30 '24

    #webdev
    #api
    #softwaredevelopment
    #testing



















    Code Smell 73 - Exceptions for Expected Cases

    Maxi Contieri ・ May 31 '21

    #webdev
    #codenewbie
    #cleancode
    #oop



















    Code Smell 244 - Incomplete Error information

    Maxi Contieri ・ Mar 31 '24

    #webdev
    #beginners
    #programming
    #tutorial



















    Code Smell 72 - Return Codes

    Maxi Contieri ・ May 28 '21

    #webdev
    #programming
    #codenewbie
    #tutorial









    More Information πŸ“•

    Wikipedia HTTP Codes


    Disclaimer πŸ“˜

    Code Smells are my opinion.





    The best error message is the one that never shows up


    Thomas Fuchs












    Software Engineering Great Quotes

    Maxi Contieri ・ Dec 28 '20

    #codenewbie
    #programming
    #quotes
    #software












    This article is part of the CodeSmell Series.












    How to Find the Stinky parts of your Code

    Maxi Contieri ・ May 21 '21

    #codenewbie
    #tutorial
    #codequality
    #beginners











    More...
Working...