From harness-claude
Guides correct HTTP status code selection for API endpoints, PR reviews, validation errors, rate limiting, and distinguishing 4xx client vs 5xx server errors.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> HTTP STATUS CODES ARE THE RESPONSE CONTRACT BETWEEN SERVER AND CLIENT — CORRECT CODE SELECTION ENABLES ERROR HANDLING, RETRY LOGIC, AND MONITORING WITHOUT PARSING RESPONSE BODIES. MISUSING STATUS CODES FORCES CLIENTS TO TREAT 200 OK AS AN AMBIGUOUS SIGNAL THAT MUST BE INSPECTED FOR HIDDEN FAILURES.
Recommends HTTP status codes for API responses in REST, GraphQL, and OpenAPI development. Generates configurations, code snippets, and best practices guidance.
Provides REST API design patterns for resource naming, URL structure, HTTP methods, status codes, pagination, filtering, error responses, versioning, and rate limiting. Use when designing endpoints or reviewing contracts.
Defines consistent API error contracts with machine-readable codes, human-readable messages, remediation advice, taxonomy, and envelope structure. Use for API design, PR reviews, style guides, audits, SDKs, and OpenAPI docs.
Share bugs, ideas, or general feedback.
HTTP STATUS CODES ARE THE RESPONSE CONTRACT BETWEEN SERVER AND CLIENT — CORRECT CODE SELECTION ENABLES ERROR HANDLING, RETRY LOGIC, AND MONITORING WITHOUT PARSING RESPONSE BODIES. MISUSING STATUS CODES FORCES CLIENTS TO TREAT 200 OK AS AN AMBIGUOUS SIGNAL THAT MUST BE INSPECTED FOR HIDDEN FAILURES.
200 OK for validation errors or internal failures404 Not Found and 403 Forbidden when a resource exists but access is denied200 or 204 after a successful mutation422 Unprocessable Entity is more appropriate than 400 Bad Request for semantic validation failures1xx Informational — Provisional responses sent before the final response. Rarely used in REST APIs. 100 Continue is sent by servers to indicate that the initial part of a request was received and the client should proceed. 101 Switching Protocols is used for WebSocket upgrades.
2xx Success — The request was received, understood, and accepted. The three most important 2xx codes:
200 OK — General success with a response body. Use for GET, PATCH, and PUT when returning the updated resource.201 Created — A new resource was created. Must include a Location header with the URL of the new resource. Use for POST and PUT-to-create.204 No Content — Success with no response body. Use for DELETE and PUT/PATCH when returning the resource is not needed.3xx Redirection — Further action is needed to complete the request. 301 Moved Permanently redirects clients and updates bookmarks. 302 Found (temporary redirect) does not update bookmarks. 304 Not Modified is the conditional GET response — see api-conditional-requests. APIs should avoid redirects in normal operation flows; they complicate client retry logic.
4xx Client Error — The request contained an error the client must fix before retrying. These are non-retryable without change. Key codes:
400 Bad Request — Malformed syntax, invalid parameters, missing required fields.401 Unauthorized — No valid authentication credentials provided. The client should re-authenticate.403 Forbidden — Authentication succeeded but the caller lacks permission. Do not leak resource existence.404 Not Found — Resource does not exist at this URL, or the server is hiding its existence (use 403 if you want to reveal it exists).409 Conflict — Request conflicts with current resource state (e.g., duplicate creation, stale optimistic lock).422 Unprocessable Entity — Request is syntactically valid but semantically invalid (e.g., end date before start date).429 Too Many Requests — Rate limit exceeded. Must include Retry-After header.5xx Server Error — The server failed to fulfill a valid request. These are potentially retryable. Key codes:
500 Internal Server Error — Unhandled exception or unexpected server failure. Do not expose stack traces.502 Bad Gateway — An upstream service returned an invalid response.503 Service Unavailable — The server is temporarily unable to handle requests. Should include Retry-After.504 Gateway Timeout — An upstream service did not respond in time.A GitHub REST API interaction demonstrating status code precision across a repository lifecycle:
Create a repository (POST → 201 Created):
POST /user/repos
Authorization: Bearer ghp_...
Content-Type: application/json
{ "name": "my-project", "private": true }
HTTP/1.1 201 Created
Location: https://api.github.com/repos/alice/my-project
Content-Type: application/json
{ "id": 123456, "name": "my-project", "full_name": "alice/my-project", ... }
Create duplicate repository (422 Unprocessable Entity — GitHub's choice):
POST /user/repos
Content-Type: application/json
{ "name": "my-project", "private": true }
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"message": "Repository creation failed.",
"errors": [{ "resource": "Repository", "code": "custom", "field": "name",
"message": "name already exists on this account" }]
}
Note: GitHub returns 422 rather than 409 for duplicate names — a documented choice that treats name uniqueness as a semantic constraint rather than a state conflict.
Fetch without credentials (401 Unauthorized):
GET /repos/alice/my-project
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="GitHub"
Content-Type: application/json
{ "message": "Requires authentication" }
Fetch a private repo with wrong credentials (403 Forbidden):
GET /repos/alice/private-project
Authorization: Bearer ghp_wrong_token
HTTP/1.1 404 Not Found
GitHub returns 404 (not 403) to avoid leaking that the private repository exists — a security pattern called "security through obscurity on existence."
Rate limit exceeded (429 Too Many Requests):
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1714780800
Content-Type: application/json
{ "message": "API rate limit exceeded for ghp_..." }
Returning 200 OK for errors. { "success": false, "error": "User not found" } in a 200 OK body breaks monitoring, alerting, and client error handling. 5xx/4xx rates in logs become meaningless. Clients must parse every body to detect failure. Fix: use the appropriate 4xx or 5xx code with an error body conforming to RFC 9457 (Problem Details) or api-problem-details-rfc.
Using 404 when 403 is correct. If a resource exists and the caller lacks permission, returning 404 (to hide existence) is a security decision that should be explicit and documented — not a default. It prevents clients from distinguishing "wrong URL" from "wrong permissions." Use 403 when the existence of the resource is not sensitive. Use 404 only when existence itself must be concealed.
Using 400 for semantic validation errors. 400 Bad Request signals a malformed request (unparseable JSON, missing Content-Type, invalid URL parameter). Semantic failures — start date after end date, referenced resource does not exist, business rule violation — belong in 422 Unprocessable Entity. This distinction helps clients route errors to the right handler: syntax errors (fix the request format) vs. semantic errors (fix the payload values).
Returning 500 for client-caused failures. An API that throws a 500 when the request body contains unexpected values is a server bug, but returning 500 to the client incorrectly signals that the server is at fault. Validate inputs early, return 400/422 for client errors, and reserve 5xx for genuine server-side failures.
This distinction is frequently confused:
401 Unauthorized means the request lacks valid authentication. The WWW-Authenticate header tells the client how to authenticate. The fix: provide credentials.403 Forbidden means authentication succeeded but authorization failed. The client is identified but not permitted. The fix: acquire the required permission or role.The naming is historical — "Unauthorized" was named before authentication and authorization were cleanly separated in practice.
409 Conflict — The request is valid but conflicts with the current state of the target resource. Use for optimistic concurrency failures (stale ETag), duplicate unique-key violations where idempotency is expected, or state machine violations (e.g., closing an already-closed order).422 Unprocessable Entity — The request is syntactically and structurally valid but fails semantic validation. Use for business rule violations, cross-field validation failures, and references to non-existent related resources.Stripe's API maps all errors to HTTP status codes with machine-readable error codes in the body:
400 for request parameter errors401 for invalid API keys402 for payment failures (a creative use of the rarely-used "Payment Required" code)403 for permission errors404 for non-existent resources409 for idempotency key reuse with different parameters429 for rate limits500/502/503 for Stripe infrastructure failuresThis precise taxonomy allows Stripe SDK clients to switch on error.type for business logic while using the HTTP status code for transport-level decisions (retry vs. no-retry). APIs that follow this pattern report 30-40% fewer support tickets related to error handling ambiguity.
201 Created + Location for resource creation, 204 No Content for mutations with no return body, 200 OK otherwise.Retry-After.500 for unhandled server failures, 503 for intentional degraded-mode responses. Never expose stack traces or internal paths in 5xx bodies.harness validate to confirm skill files are well-formed.200 OK for error conditions — all errors use appropriate 4xx or 5xx codes.201 Created with a Location header.401 and 403 are used for authentication vs. authorization failure respectively, with documented rationale for any security-through-obscurity 404 substitutions.429 responses include a Retry-After header; 503 responses include Retry-After when a recovery time is known.400 is reserved for malformed requests; semantic validation failures use 422 Unprocessable Entity.