Three gaps that all showed up as something broken on screen. An uploaded image could never be fetched. Every asset address the backend builds pointed at a path nothing served, so a figure was missing in the Studio preview and in the published page alike. There is now a public media endpoint under the prefix that is already anonymous and already routed, so no edge configuration has to change for an image to appear. Only READY assets are served; narrowing it to assets a published record uses would break previewing, which is the one thing an author does before publishing. Deleting a working copy left wreckage. Publications, validations and previews address a document by (kind, id) with no foreign key, so deleting one left rows pointing at nothing — and the dashboard and publication history read a title off that nothing and crashed. Both screens went down in production this way. A record with publication history is now refused outright, because that history says what was once public; validations and previews go with the record, because they are its scaffolding. A Decision could not be deleted at all while every other kind could. It can now, under the same two rules, and refuses when another decision supersedes it or the home page features it — neither cascades, so deleting would have been a foreign-key violation reaching the author as a 500. The persistence tests cover every one of these queries against real PostgreSQL. That suite exists because a column name I assumed rather than checked reached production once already; three more assumptions were caught here before it could happen again.
1496 lines
62 KiB
YAML
1496 lines
62 KiB
YAML
# Registry: Error Codes
|
|
# SSOT: wiki/projects/ca-tmpl/registries/error-codes.yaml
|
|
# Schema owner: feature-contract-registry-governance
|
|
# Category enum owner: feature-operational-error-observability-foundation
|
|
# Last updated: 2026-05-22
|
|
# Note: 이 파일은 Phase B 산출물. Phase C2(ca-tmpl 실 코드)에서 generated Java constants의 source.
|
|
#
|
|
# Schema (per row):
|
|
# code: UPPER_SNAKE_CASE
|
|
# category: VALIDATION | AUTH | AUTHZ | NOT_FOUND | CONFLICT |
|
|
# RATE_LIMIT | TRANSIENT_DEPENDENCY | PERMANENT_DEPENDENCY |
|
|
# DATA_INTEGRITY | INTERNAL
|
|
# http_status: int (async-only failures use 500 placeholder)
|
|
# retryable: bool
|
|
# retry_after_seconds: int | null (RATE_LIMIT/TRANSIENT 권고 backoff)
|
|
# owner_branch: source branch (raw/branch-notes/feature-*.md)
|
|
# owner_layer: presentation | application | domain | infrastructure | crosscut
|
|
# client_safe_message: no token / no principal raw / no internal path / no stack trace
|
|
# log_level: ERROR | WARN | INFO
|
|
# runbook_link: runbook://area/scenario OR null (client-error만 null 허용)
|
|
# compatibility_impact: none | additive | behavior-change | breaking
|
|
# required_test: owning contract test identifier
|
|
#
|
|
# Runbook policy (operational-runbook-contract L80):
|
|
# retryable=false + category ∈ {AUTH, AUTHZ, RATE_LIMIT, INTERNAL,
|
|
# TRANSIENT_DEPENDENCY, PERMANENT_DEPENDENCY} ⇒ runbook_link 필수.
|
|
# VALIDATION/NOT_FOUND/CONFLICT/DATA_INTEGRITY는 client-error로 runbook 면제 가능.
|
|
# retryable=true 인 모든 row는 runbook_link 필수.
|
|
|
|
errors:
|
|
|
|
# ============================================================
|
|
# AUTH (feature-security-operational-baseline / Decision Matrix)
|
|
# ============================================================
|
|
|
|
# source: feature-security-operational-baseline L82 — "token 누락 | 401 | AUTH_TOKEN_MISSING | AUTH"
|
|
- code: AUTH_TOKEN_MISSING
|
|
category: AUTH
|
|
http_status: 401
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-security-operational-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Authentication required"
|
|
log_level: WARN
|
|
runbook_link: "runbook://auth/token-missing"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:auth-category
|
|
|
|
# source: feature-security-operational-baseline L83 — "token malformed (parse fail) | 401 | AUTH_TOKEN_MALFORMED | AUTH"
|
|
- code: AUTH_TOKEN_MALFORMED
|
|
category: AUTH
|
|
http_status: 401
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-security-operational-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Authentication failed"
|
|
log_level: WARN
|
|
runbook_link: "runbook://auth/token-malformed"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:auth-category
|
|
|
|
# source: feature-security-operational-baseline L84 — "token expired (clock skew tolerance 60s 초과) | 401 | AUTH_TOKEN_EXPIRED | AUTH"
|
|
- code: AUTH_TOKEN_EXPIRED
|
|
category: AUTH
|
|
http_status: 401
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-security-operational-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Authentication expired"
|
|
log_level: WARN
|
|
runbook_link: "runbook://auth/token-expired"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:auth-category
|
|
|
|
# source: feature-security-operational-baseline L85 — "invalid signature | 401 | AUTH_TOKEN_INVALID_SIGNATURE | AUTH"
|
|
- code: AUTH_TOKEN_INVALID_SIGNATURE
|
|
category: AUTH
|
|
http_status: 401
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-security-operational-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Authentication failed"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://auth/token-invalid-signature"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:auth-category
|
|
|
|
# source: feature-security-operational-baseline L86 — "issuer mismatch | 401 | AUTH_ISSUER_MISMATCH | AUTH"
|
|
- code: AUTH_ISSUER_MISMATCH
|
|
category: AUTH
|
|
http_status: 401
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-security-operational-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Authentication failed"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://auth/issuer-mismatch"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:auth-category
|
|
|
|
# source: feature-security-operational-baseline L87 — "audience mismatch | 401 | AUTH_AUDIENCE_MISMATCH | AUTH"
|
|
- code: AUTH_AUDIENCE_MISMATCH
|
|
category: AUTH
|
|
http_status: 401
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-security-operational-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Authentication failed"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://auth/audience-mismatch"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:auth-category
|
|
|
|
# source: feature-security-operational-baseline L88 — "unknown kid (JWKS 미캐시) | 401 + Retry-After 5s | AUTH_KID_UNKNOWN | AUTH"
|
|
- code: AUTH_KID_UNKNOWN
|
|
category: AUTH
|
|
http_status: 401
|
|
retryable: true # 2026-06-01: false→true. JWKS 키 회전 중 unknown kid 는 ~5s 후 JWKS refresh 로 해소 가능(transient). retry_after_seconds=5 + client_safe_message "please retry" 와 정합. 키 고정 정책으로 전환 시 false 복귀.
|
|
retry_after_seconds: 5
|
|
owner_branch: feature-security-operational-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Authentication failed, please retry"
|
|
log_level: WARN
|
|
runbook_link: "runbook://auth/kid-unknown"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:auth-category
|
|
|
|
# source: feature-security-operational-baseline L89 — "JWKS endpoint outage ... | AUTH_JWKS_UNAVAILABLE | TRANSIENT_DEPENDENCY"
|
|
- code: AUTH_JWKS_UNAVAILABLE
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 503
|
|
retryable: true
|
|
retry_after_seconds: 30
|
|
owner_branch: feature-security-operational-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Authentication service temporarily unavailable"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://auth/jwks-unavailable"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:auth-category
|
|
|
|
# source: feature-security-operational-baseline L90 — "claim mapping failure ... | 401 | AUTH_CLAIM_MAPPING_FAILED | AUTH"
|
|
- code: AUTH_CLAIM_MAPPING_FAILED
|
|
category: AUTH
|
|
http_status: 401
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-security-operational-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Authentication failed"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://auth/claim-mapping-failed"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:auth-category
|
|
|
|
# ============================================================
|
|
# AUTHZ (feature-security-operational-baseline)
|
|
# ============================================================
|
|
|
|
# source: feature-security-operational-baseline L91 — "valid token + 권한 부족 | 403 | AUTHZ_INSUFFICIENT_PERMISSION | AUTHZ"
|
|
- code: AUTHZ_INSUFFICIENT_PERMISSION
|
|
category: AUTHZ
|
|
http_status: 403
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-security-operational-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Permission denied"
|
|
log_level: WARN
|
|
runbook_link: "runbook://authz/insufficient-permission"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:authz-category
|
|
|
|
# source: feature-security-operational-baseline L92 — "valid token + tenant cross-access | 403 | AUTHZ_TENANT_MISMATCH | AUTHZ"
|
|
- code: AUTHZ_TENANT_MISMATCH
|
|
category: AUTHZ
|
|
http_status: 403
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-security-operational-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Permission denied"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://authz/tenant-mismatch"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:authz-category
|
|
|
|
# ============================================================
|
|
# INTERNAL (feature-security-operational-baseline + container-runtime)
|
|
# ============================================================
|
|
|
|
# source: feature-security-operational-baseline L93 — "public path misconfiguration ... | 500 + P1 alert | INTERNAL_AUTH_MISCONFIGURATION | INTERNAL"
|
|
- code: INTERNAL_AUTH_MISCONFIGURATION
|
|
category: INTERNAL
|
|
http_status: 500
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-security-operational-baseline
|
|
owner_layer: crosscut
|
|
client_safe_message: "Internal server error"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://auth/public-path-misconfiguration"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:auth-category
|
|
|
|
# source: feature-container-runtime-contract L113 — "JVM OutOfMemoryError → ExitOnOutOfMemoryError로 137 exit, log에 error.code=JVM_OOM 명시"
|
|
- code: JVM_OOM
|
|
category: INTERNAL
|
|
http_status: 500
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-container-runtime-contract
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Internal server error"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://runtime/jvm-oom"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:container-runtime-oom
|
|
|
|
# ============================================================
|
|
# DB / Persistence (feature-persistence-failure-baseline / SQLState Matrix)
|
|
# ============================================================
|
|
|
|
# source: feature-persistence-failure-baseline L85 — "08* | all | TRANSIENT_DEPENDENCY | DB_UNAVAILABLE | true"
|
|
- code: DB_UNAVAILABLE
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 503
|
|
retryable: true
|
|
retry_after_seconds: 5
|
|
owner_branch: feature-persistence-failure-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Service temporarily unavailable"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://db/unavailable"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:persistence-mapping
|
|
|
|
# source: feature-persistence-failure-baseline L86 — "40001 | Postgres/MySQL | CONFLICT | DB_SERIALIZATION_FAILURE | true"
|
|
- code: DB_SERIALIZATION_FAILURE
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: true
|
|
retry_after_seconds: 1
|
|
owner_branch: feature-persistence-failure-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Request conflicted with another transaction, please retry"
|
|
log_level: WARN
|
|
runbook_link: "runbook://db/serialization-failure"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:persistence-mapping
|
|
|
|
# source: feature-persistence-failure-baseline L87 — "40P01 | Postgres | CONFLICT | DB_DEADLOCK | true (backoff)"
|
|
- code: DB_DEADLOCK
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: true
|
|
retry_after_seconds: 1
|
|
owner_branch: feature-persistence-failure-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Request conflicted, please retry"
|
|
log_level: WARN
|
|
runbook_link: "runbook://db/deadlock"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:persistence-mapping
|
|
|
|
# source: feature-persistence-failure-baseline L88 — "23502 | Postgres | DATA_INTEGRITY | DB_NULL_VIOLATION | false"
|
|
- code: DB_NULL_VIOLATION
|
|
category: DATA_INTEGRITY
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-persistence-failure-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Request violates a required field constraint"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:persistence-mapping
|
|
|
|
# source: feature-persistence-failure-baseline L89 — "23503 | Postgres | DATA_INTEGRITY | DB_FK_VIOLATION | false"
|
|
- code: DB_FK_VIOLATION
|
|
category: DATA_INTEGRITY
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-persistence-failure-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Request references missing resource"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:persistence-mapping
|
|
|
|
# source: feature-persistence-failure-baseline L90 — "23505 | Postgres | CONFLICT | DB_UNIQUE_VIOLATION | false (business mapping)"
|
|
- code: DB_UNIQUE_VIOLATION
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-persistence-failure-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Resource already exists"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:persistence-mapping
|
|
|
|
# source: feature-persistence-failure-baseline L91 — "23514 | Postgres | DATA_INTEGRITY | DB_CHECK_VIOLATION | false"
|
|
- code: DB_CHECK_VIOLATION
|
|
category: DATA_INTEGRITY
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-persistence-failure-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Request violates a value constraint"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:persistence-mapping
|
|
|
|
# source: feature-persistence-failure-baseline L92 — "25P03 | Postgres | TRANSIENT_DEPENDENCY | DB_IDLE_IN_TX_TIMEOUT | true"
|
|
- code: DB_IDLE_IN_TX_TIMEOUT
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 503
|
|
retryable: true
|
|
retry_after_seconds: 2
|
|
owner_branch: feature-persistence-failure-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Service temporarily unavailable"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://db/idle-in-tx-timeout"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:persistence-mapping
|
|
|
|
# source: feature-persistence-failure-baseline L93 — "57014 | Postgres | TRANSIENT_DEPENDENCY | DB_QUERY_CANCELED | false"
|
|
- code: DB_QUERY_CANCELED
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 503
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-persistence-failure-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Request was canceled, please retry later"
|
|
log_level: WARN
|
|
runbook_link: "runbook://db/query-canceled"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:persistence-mapping
|
|
|
|
# ============================================================
|
|
# Rate limit / Idempotency (feature-rate-limit-idempotency-contract)
|
|
# ============================================================
|
|
|
|
# source: feature-rate-limit-idempotency-contract — rate limit response/log 기준 / Retry-After header 기준 (scope L29, L33)
|
|
- code: RATE_LIMIT_EXCEEDED
|
|
category: RATE_LIMIT
|
|
http_status: 429
|
|
retryable: true
|
|
retry_after_seconds: 1
|
|
owner_branch: feature-rate-limit-idempotency-contract
|
|
owner_layer: presentation
|
|
client_safe_message: "Too many requests, please retry after the indicated interval"
|
|
log_level: WARN
|
|
runbook_link: "runbook://rate-limit/exceeded"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:rate-limit
|
|
|
|
# source: feature-rate-limit-idempotency-contract L71 — "200ms 초과 시 409 IDEMPOTENT_IN_FLIGHT (retryable=false, client는 polling)"
|
|
- code: IDEMPOTENT_IN_FLIGHT
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-rate-limit-idempotency-contract
|
|
owner_layer: application
|
|
client_safe_message: "A previous identical request is still being processed, please poll for result"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:idempotency
|
|
|
|
# source: feature-rate-limit-idempotency-contract L72 — "fingerprint mismatch (same key + different body) = 422 IDEMPOTENT_REQUEST_MISMATCH"
|
|
- code: IDEMPOTENT_REQUEST_MISMATCH
|
|
category: VALIDATION
|
|
http_status: 422
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-rate-limit-idempotency-contract
|
|
owner_layer: application
|
|
client_safe_message: "Idempotency key reused with different request body"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:idempotency
|
|
|
|
# ============================================================
|
|
# File / Resource (feature-file-resource-handling-contract)
|
|
# ============================================================
|
|
|
|
# source: feature-file-resource-handling-contract L69 — "spring.servlet.multipart.max-file-size 10MB ... Spring 단의 enforcement가 실패 시 envelope 응답 보장" / 테스트 계약 "oversized upload가 generic 500으로 처리되면 실패"
|
|
- code: UPLOAD_SIZE_EXCEEDED
|
|
category: VALIDATION
|
|
http_status: 413
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-file-resource-handling-contract
|
|
owner_layer: presentation
|
|
client_safe_message: "Uploaded file exceeds maximum size"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:file-upload
|
|
|
|
# source: feature-file-resource-handling-contract L72 — "allowed content-type allowlist starting set ..."
|
|
- code: UPLOAD_CONTENT_TYPE_REJECTED
|
|
category: VALIDATION
|
|
http_status: 415
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-file-resource-handling-contract
|
|
owner_layer: presentation
|
|
client_safe_message: "Uploaded content type is not allowed"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:file-upload
|
|
|
|
# source: feature-file-resource-handling-contract — Decisionized Work Items "path traversal | normalized storage key only ... | traversal test"
|
|
- code: PATH_TRAVERSAL_DETECTED
|
|
category: VALIDATION
|
|
http_status: 400
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-file-resource-handling-contract
|
|
owner_layer: presentation
|
|
client_safe_message: "Invalid file path"
|
|
log_level: ERROR
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:file-upload
|
|
|
|
# source: feature-file-resource-handling-contract L73 — "streaming download backpressure = response timeout 60s, max stream 100MB. 초과 시 truncate + ERROR log"
|
|
- code: DOWNLOAD_STREAMING_FAILURE
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 503
|
|
retryable: true
|
|
retry_after_seconds: 5
|
|
owner_branch: feature-file-resource-handling-contract
|
|
owner_layer: presentation
|
|
client_safe_message: "Download failed, please retry"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://file/download-streaming-failure"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:file-download
|
|
|
|
# ============================================================
|
|
# API contract transport-standard codes (feature-api-contract-baseline)
|
|
# ============================================================
|
|
# NOTE: feature-api-contract-baseline owns the transport-shape failure
|
|
# classification (D8 413/414, D9 406/415, D12 405, D15 412). These rows mirror
|
|
# dev.caskeleton.shared.error.OperationalError; the D11 status-mapping
|
|
# consistency test (owner: this branch, producer) fails the build when a code's
|
|
# registry http_status and the enum httpStatus() drift apart.
|
|
|
|
# source: feature-api-contract-baseline.md D12 — "405 Method Not Allowed + Allow header 의무"
|
|
- code: METHOD_NOT_ALLOWED
|
|
category: VALIDATION
|
|
http_status: 405
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-api-contract-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "HTTP method not allowed for this resource"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:api-contract-status-mapping
|
|
|
|
# source: feature-api-contract-baseline.md D9 — "406 Not Acceptable = 응답 표현 협상 실패"
|
|
- code: NOT_ACCEPTABLE
|
|
category: VALIDATION
|
|
http_status: 406
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-api-contract-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "No acceptable representation for the requested Accept header"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:api-contract-status-mapping
|
|
|
|
# source: feature-api-contract-baseline.md D15 — "If-Match mismatch 시 412 Precondition Failed"
|
|
- code: PRECONDITION_FAILED
|
|
category: CONFLICT
|
|
http_status: 412
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-api-contract-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Resource was modified by another request; refetch and retry"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:api-contract-status-mapping
|
|
|
|
# source: feature-api-contract-baseline.md D8 — "request size limit 실패 분류 (413)"
|
|
- code: PAYLOAD_TOO_LARGE
|
|
category: VALIDATION
|
|
http_status: 413
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-api-contract-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Request payload is too large"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:api-contract-status-mapping
|
|
|
|
# source: feature-api-contract-baseline.md D8 형제 — "URI 길이 실패 분류 (414)"
|
|
# NOTE: enforcement is Tomcat/gateway-owned (rejected before Spring dispatch);
|
|
# this row + code exist for status-mapping consistency. End-to-end 414 contract
|
|
# test is `planned` (gateway/Tomcat maxHttpHeaderSize 8KB boundary).
|
|
- code: URI_TOO_LONG
|
|
category: VALIDATION
|
|
http_status: 414
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-api-contract-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Request URI is too long"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:api-contract-status-mapping
|
|
|
|
# source: feature-api-contract-baseline.md D9 — "415 Unsupported Media Type = 요청 본문 format 미지원"
|
|
- code: UNSUPPORTED_MEDIA_TYPE
|
|
category: VALIDATION
|
|
http_status: 415
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-api-contract-baseline
|
|
owner_layer: presentation
|
|
client_safe_message: "Request Content-Type is not supported"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:api-contract-status-mapping
|
|
|
|
# ============================================================
|
|
# Tenant (feature-tenant-context-policy)
|
|
# ============================================================
|
|
|
|
# source: feature-tenant-context-policy L71 — "tenant 미지원 모드에서 X-Tenant-Id 헤더 수신 시 400 TENANT_NOT_SUPPORTED (filter 단계)"
|
|
- code: TENANT_NOT_SUPPORTED
|
|
category: VALIDATION
|
|
http_status: 400
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-tenant-context-policy
|
|
owner_layer: presentation
|
|
client_safe_message: "Tenant context is not supported by this deployment"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:tenant-policy
|
|
|
|
# ============================================================
|
|
# Validation / Business rule (feature-business-rule-validation-contract)
|
|
# ============================================================
|
|
# NOTE: business-rule-validation branch는 mapping 규칙 SSOT (syntax→VALIDATION,
|
|
# policy→AUTHZ/CONFLICT, invariant→CONFLICT/VALIDATION, persistence→PERSISTENCE/CONFLICT)
|
|
# 이며 구체 code는 example로 VALIDATION_EMAIL_FORMAT만 등장
|
|
# (feature-operational-error-observability-foundation L110). 실제 도메인별 code는
|
|
# Phase D(도메인 feature 적용) 시 본 registry에 추가.
|
|
|
|
# source: feature-operational-error-observability-foundation L110 — "code: VALIDATION_EMAIL_FORMAT, // registry-registered code" (validation field error JSON shape example)
|
|
- code: VALIDATION_EMAIL_FORMAT
|
|
category: VALIDATION
|
|
http_status: 400
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-operational-error-observability-foundation
|
|
owner_layer: presentation
|
|
client_safe_message: "Invalid email format"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: none
|
|
required_test: contract-verification:validation-envelope
|
|
|
|
# ============================================================
|
|
# Cache (feature-cache-consistency-contract)
|
|
# ============================================================
|
|
|
|
# source: feature-cache-consistency-contract — Decisionized Work Items "Redis unavailable | degrade only if declared | fail-fast for required cache | generic INTERNAL | unavailable mapping" / 테스트 "Redis unavailable이 degrade 가능 여부 없이 INTERNAL로 처리되면 실패"
|
|
- code: CACHE_UNAVAILABLE
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 503
|
|
retryable: true
|
|
retry_after_seconds: 2
|
|
owner_branch: feature-cache-consistency-contract
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Service temporarily unavailable"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://cache/unavailable"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:cache-consistency
|
|
|
|
# source: feature-cache-consistency-contract L70 — "stampede 방지 default = single-instance Caffeine local lock, multi-instance HPA 시 Redisson RLock distributed mutex" / 테스트 "동일 key에 대해 동시 cache miss 시 backend 호출이 1회로 제한되는지 verify (stampede). 미충족 시 실패"
|
|
- code: CACHE_STAMPEDE_LOCK_TIMEOUT
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 503
|
|
retryable: true
|
|
retry_after_seconds: 1
|
|
owner_branch: feature-cache-consistency-contract
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Service temporarily unavailable"
|
|
log_level: WARN
|
|
runbook_link: "runbook://cache/stampede-lock-timeout"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:cache-consistency
|
|
|
|
# ============================================================
|
|
# Outbound HTTP (feature-outbound-http-client-baseline)
|
|
# ============================================================
|
|
|
|
# source: feature-outbound-http-client-baseline L70 — "outbound HTTP timeout default = connect 2s / read 5s / global call 10s" + scope "timeout/connect/DNS failure 분류" / 테스트 "upstream timeout은 retryable dependency failure로 분류되어야 함"
|
|
- code: DEPENDENCY_TIMEOUT
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 504
|
|
retryable: true
|
|
retry_after_seconds: 2
|
|
owner_branch: feature-outbound-http-client-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Upstream service did not respond in time, please retry"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://dependency/timeout"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:outbound-http
|
|
|
|
# source: feature-outbound-http-client-baseline — scope "timeout/connect/DNS failure 분류" + L70 connect=2s timeout
|
|
- code: DEPENDENCY_CONNECT_FAILED
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 503
|
|
retryable: true
|
|
retry_after_seconds: 2
|
|
owner_branch: feature-outbound-http-client-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Upstream service unreachable, please retry"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://dependency/connect-failed"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:outbound-http
|
|
|
|
# source: feature-outbound-http-client-baseline — scope "timeout/connect/DNS failure 분류"
|
|
- code: DEPENDENCY_DNS_FAILED
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 503
|
|
retryable: true
|
|
retry_after_seconds: 5
|
|
owner_branch: feature-outbound-http-client-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Upstream service unreachable, please retry"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://dependency/dns-failed"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:outbound-http
|
|
|
|
# source: feature-outbound-http-client-baseline — scope "upstream 4xx/5xx 분류" / 테스트 "401/403은 credential/scope/config 문제로 분류되어야 함"
|
|
- code: DEPENDENCY_4XX_CLIENT
|
|
category: PERMANENT_DEPENDENCY
|
|
http_status: 502
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-outbound-http-client-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Upstream service rejected the request"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://dependency/4xx-client"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:outbound-http
|
|
|
|
# source: feature-outbound-http-client-baseline — scope "upstream 4xx/5xx 분류"
|
|
- code: DEPENDENCY_5XX_SERVER
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 502
|
|
retryable: true
|
|
retry_after_seconds: 2
|
|
owner_branch: feature-outbound-http-client-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Upstream service error, please retry"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://dependency/5xx-server"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:outbound-http
|
|
|
|
# source: feature-outbound-http-client-baseline L69 — "circuit breaker metric은 dependency.name, dependency.type, outcome까지만 tag로 허용" + Decisionized "circuit breaker | Resilience4j optional env"
|
|
- code: DEPENDENCY_CIRCUIT_OPEN
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 503
|
|
retryable: true
|
|
retry_after_seconds: 10
|
|
owner_branch: feature-outbound-http-client-baseline
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Upstream service temporarily unavailable, please retry later"
|
|
log_level: WARN
|
|
runbook_link: "runbook://dependency/circuit-open"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:outbound-http
|
|
|
|
# ============================================================
|
|
# Outbox (feature-domain-event-outbox-contract)
|
|
# ============================================================
|
|
|
|
# source: feature-domain-event-outbox-contract L67 — "outbox row status enum = PENDING / IN_FLIGHT / PUBLISHED / FAILED / DEAD" + scope "publish 실패 분류" / 판정 "publish 실패가 retry/DLQ/log/runbook 기준 없이 삼켜지면 실패"
|
|
- code: OUTBOX_PUBLISH_FAILED
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 500
|
|
retryable: true
|
|
retry_after_seconds: 30
|
|
owner_branch: feature-domain-event-outbox-contract
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Internal server error"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://outbox/publish-failed"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:outbox-publish
|
|
|
|
# source: feature-domain-event-outbox-contract L67 — outbox status enum "DEAD" / Outbox Defaults "DLQ | background-job branch owner"
|
|
- code: OUTBOX_DEAD_LETTER
|
|
category: INTERNAL
|
|
http_status: 500
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-domain-event-outbox-contract
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Internal server error"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://outbox/dead-letter"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:outbox-dlq
|
|
|
|
# ============================================================
|
|
# Background job / Async (feature-background-job-async-contract)
|
|
# ============================================================
|
|
|
|
# source: feature-background-job-async-contract — Decisionized "saturation | bounded executor + rejection log" / L72 "saturation policy default = AbortPolicy" / 테스트 "executor rejection이 structured log 없이 발생하면 실패"
|
|
- code: JOB_EXECUTOR_REJECTED
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 503
|
|
retryable: true
|
|
retry_after_seconds: 5
|
|
owner_branch: feature-background-job-async-contract
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Service temporarily unavailable"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://job/executor-rejected"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:async-saturation
|
|
|
|
# source: feature-background-job-async-contract L69 — "기본 backoff는 exponential backoff with jitter, max attempts 3, DLQ after exhausted attempts" + scope "shutdown 중 job 처리 기준" / L73 graceful shutdown ≤19s
|
|
- code: JOB_TIMEOUT
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 500
|
|
retryable: true
|
|
retry_after_seconds: 10
|
|
owner_branch: feature-background-job-async-contract
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Internal server error"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://job/timeout"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:async-timeout
|
|
|
|
# source: feature-background-job-async-contract L69 — "DLQ after exhausted attempts" + Decisionized "retry/DLQ | exp backoff jitter, max 3, DLQ exhausted | ... | infinite retry"
|
|
- code: JOB_DEAD_LETTER
|
|
category: INTERNAL
|
|
http_status: 500
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-background-job-async-contract
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Internal server error"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://job/dead-letter"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:async-dlq
|
|
|
|
# ============================================================
|
|
# Distributed Lock (feature-distributed-lock-contract)
|
|
# ============================================================
|
|
|
|
# source: feature-distributed-lock-contract D7 — "lock 획득 실패/timeout 의 error code =
|
|
# LOCK_ACQUISITION_TIMEOUT (category CONFLICT, retryable true, client_safe true) + metric
|
|
# lock.acquisition" / D5 — "try-lock + 유한 waitTime + lease(TTL) 필수, 무한 blocking 금지".
|
|
# category CONFLICT 는 기존 enum 재사용; retryable=true — 락 보유자가 임계 구역을 빠져나오면
|
|
# 동일 요청 재시도로 해소된다(transient contention). DB_DEADLOCK / DB_SERIALIZATION_FAILURE 와
|
|
# 같은 retryable CONFLICT 계열(409). 본 코드는 distributedLockProvider 획득 timeout 전용이며
|
|
# cache stampede lock 의 CACHE_STAMPEDE_LOCK_TIMEOUT(cache-consistency, TRANSIENT_DEPENDENCY 503)
|
|
# 과 의미가 구분된다 — 후자는 캐시 백엔드 의존성 timeout, 전자는 분산 상호배제 contention.
|
|
- code: LOCK_ACQUISITION_TIMEOUT
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: true
|
|
retry_after_seconds: 1
|
|
owner_branch: feature-distributed-lock-contract
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Resource is busy, please retry"
|
|
log_level: WARN
|
|
runbook_link: "runbook://lock/acquisition-timeout"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:lock-acquisition-timeout
|
|
|
|
# ============================================================
|
|
# Migration / Startup (feature-migration-startup-contract)
|
|
# ============================================================
|
|
|
|
# source: feature-migration-startup-contract L71 — "startup exit code 표준 = ... migration 실패=70 ..." + Decisionized "startup failure log | structured log with startup.phase, error.code, error.category"
|
|
- code: MIGRATION_FAILED
|
|
category: INTERNAL
|
|
http_status: 500
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-migration-startup-contract
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Internal server error"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://migration/failed"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:migration-startup
|
|
|
|
# source: feature-migration-startup-contract L71 — "startup exit code 표준 = env 누락/malformed=78 ..." / 테스트 "required env 누락 시 startup이 성공하면 실패"
|
|
- code: STARTUP_VALIDATION_FAILED
|
|
category: INTERNAL
|
|
http_status: 500
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-migration-startup-contract
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Internal server error"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://startup/validation-failed"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:migration-startup
|
|
|
|
# source: feature-migration-startup-contract L71 — "startup exit code 표준 = ... required adapter disabled=72" / 테스트 "disabled required adapter로 app이 뜨면 실패"
|
|
- code: REQUIRED_ADAPTER_DISABLED
|
|
category: INTERNAL
|
|
http_status: 500
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-migration-startup-contract
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Internal server error"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://startup/required-adapter-disabled"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:migration-startup
|
|
|
|
# source: feature-integration-adapter-templates §구현 가이드 §4 (Layer 3) + §Audit A2.
|
|
# Runtime-lifecycle fail-fast for an invoke against a DISABLED optional adapter
|
|
# (Kafka/Redis/Slack/Google Email). Deliberately distinct from the startup-lifecycle
|
|
# REQUIRED_ADAPTER_DISABLED above (exit 72): a runtime invoke ≠ a startup validation,
|
|
# so reusing the startup code would conflate two lifecycles (A2 resolution — new
|
|
# runtime code owned by this branch). retryable=false: the adapter stays disabled
|
|
# until redeploy, so retrying the same call never clears it.
|
|
- code: ADAPTER_DISABLED
|
|
category: INTERNAL
|
|
http_status: 500
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-integration-adapter-templates
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Internal server error"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://adapter/adapter-disabled"
|
|
compatibility_impact: none
|
|
required_test: adapter-contract:adapter-disabled-runtime-call
|
|
|
|
# source: feature-migration-startup-contract L71 — "startup exit code 표준 = ... profile mismatch=71" / 테스트 "prod profile에서 local-only 설정이 켜지면 실패"
|
|
- code: PROFILE_MISMATCH
|
|
category: INTERNAL
|
|
http_status: 500
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-migration-startup-contract
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Internal server error"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://startup/profile-mismatch"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:migration-startup
|
|
|
|
# ============================================================
|
|
# Management / Actuator (feature-management-actuator-security-contract)
|
|
# ============================================================
|
|
|
|
# source: feature-management-actuator-security-contract — Exposure Policy "env/configprops | forbidden" "heapdump/threaddump | forbidden unless break-glass runbook" "shutdown | forbidden" / 테스트 "prod에서 env/configprops endpoint가 노출되면 실패"
|
|
- code: ACTUATOR_FORBIDDEN
|
|
category: AUTHZ
|
|
http_status: 403
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-management-actuator-security-contract
|
|
owner_layer: presentation
|
|
client_safe_message: "Permission denied"
|
|
log_level: WARN
|
|
runbook_link: "runbook://management/actuator-forbidden"
|
|
compatibility_impact: none
|
|
required_test: contract-verification:management-actuator
|
|
|
|
# ============================================================
|
|
# TECH LOG STUDIO (studio-v1.yaml ApiError.code — 23종, feature-techlog-studio-backend)
|
|
#
|
|
# StudioError(dev.caskeleton.application.techlog.error.StudioError)와 1:1 매핑.
|
|
# category/http_status/retryable은 그 enum의 선언과 정확히 같아야 한다
|
|
# (StudioErrorRegistryTest가 코드 존재만 보고, 값 일치는 이 파일의 리뷰 책임).
|
|
#
|
|
# PAYLOAD_TOO_LARGE / UNSUPPORTED_MEDIA_TYPE은 StudioError에도 있지만 별도 row를
|
|
# 추가하지 않는다 — feature-api-contract-baseline이 이미 동일 code로 아래(L514,
|
|
# L545)에 VALIDATION/413/415/false, VALIDATION/415/false row를 갖고 있고 값이
|
|
# StudioError 선언과 정확히 일치한다. error-codes.yaml의 identity column은 `code`
|
|
# 하나뿐이라 같은 code로 두 번째 row를 추가하면 ContractRegistrySchemaGovernanceTest
|
|
# 의 "duplicate identity" 게이트가 깨진다. 즉 이 두 코드는 기존 row가 이미 커버한다.
|
|
# ============================================================
|
|
|
|
# source: studio-v1.yaml ApiError.code — AUTHENTICATION_REQUIRED (StudioError.AUTHENTICATION_REQUIRED)
|
|
- code: AUTHENTICATION_REQUIRED
|
|
category: AUTH
|
|
http_status: 401
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: presentation
|
|
client_safe_message: "Studio 인증이 필요합니다"
|
|
log_level: INFO
|
|
runbook_link: "runbook://auth/token-missing"
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — STUDIO_ACCESS_DENIED (StudioError.STUDIO_ACCESS_DENIED)
|
|
- code: STUDIO_ACCESS_DENIED
|
|
category: AUTHZ
|
|
http_status: 403
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "이 Studio 리소스에 접근할 권한이 없습니다"
|
|
log_level: WARN
|
|
runbook_link: "runbook://authz/insufficient-permission"
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — DOCUMENT_NOT_FOUND (StudioError.DOCUMENT_NOT_FOUND)
|
|
- code: DOCUMENT_NOT_FOUND
|
|
category: NOT_FOUND
|
|
http_status: 404
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "요청한 문서를 찾을 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — VERSION_CONFLICT (StudioError.VERSION_CONFLICT)
|
|
- code: VERSION_CONFLICT
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "저장된 version이 더 최신입니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — REQUEST_VALIDATION_FAILED (StudioError.REQUEST_VALIDATION_FAILED)
|
|
- code: REQUEST_VALIDATION_FAILED
|
|
category: VALIDATION
|
|
http_status: 422
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: presentation
|
|
client_safe_message: "요청 형식이 올바르지 않습니다"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — DOCUMENT_VALIDATION_FAILED
|
|
# (StudioError.DOCUMENT_VALIDATION_FAILED). 원래 계약 이름은 VALIDATION_FAILED였으나
|
|
# 스켈레톤 전역 OperationalError.VALIDATION_FAILED(400, VALIDATION)와 code 문자열이
|
|
# 충돌해(같은 문자열, 다른 http_status) 개명했다 — controller 판정, 2026-08-19.
|
|
- code: DOCUMENT_VALIDATION_FAILED
|
|
category: VALIDATION
|
|
http_status: 422
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "문서 검증에 실패했습니다"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — VALIDATION_STALE (StudioError.VALIDATION_STALE)
|
|
- code: VALIDATION_STALE
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "검증 결과가 최신 문서 기준이 아닙니다. 다시 검증해 주세요"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — PREVIEW_NOT_FOUND (StudioError.PREVIEW_NOT_FOUND)
|
|
- code: PREVIEW_NOT_FOUND
|
|
category: NOT_FOUND
|
|
http_status: 404
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "요청한 미리보기를 찾을 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — PREVIEW_STALE (StudioError.PREVIEW_STALE)
|
|
- code: PREVIEW_STALE
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "미리보기가 최신 문서 기준이 아닙니다. 다시 생성해 주세요"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — PREVIEW_EXPIRED (StudioError.PREVIEW_EXPIRED)
|
|
- code: PREVIEW_EXPIRED
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "미리보기가 만료되었습니다. 다시 생성해 주세요"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — PUBLICATION_NOT_FOUND (StudioError.PUBLICATION_NOT_FOUND)
|
|
- code: PUBLICATION_NOT_FOUND
|
|
category: NOT_FOUND
|
|
http_status: 404
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "요청한 게시물을 찾을 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — PUBLICATION_CONFLICT (StudioError.PUBLICATION_CONFLICT)
|
|
- code: PUBLICATION_CONFLICT
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "게시 작업이 다른 변경과 충돌했습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — PUBLICATION_EVENT_NOT_FOUND (StudioError.PUBLICATION_EVENT_NOT_FOUND)
|
|
- code: PUBLICATION_EVENT_NOT_FOUND
|
|
category: NOT_FOUND
|
|
http_status: 404
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "요청한 게시 이벤트를 찾을 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — PUBLICATION_SNAPSHOT_NOT_FOUND (StudioError.PUBLICATION_SNAPSHOT_NOT_FOUND)
|
|
- code: PUBLICATION_SNAPSHOT_NOT_FOUND
|
|
category: NOT_FOUND
|
|
http_status: 404
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "요청한 게시 스냅샷을 찾을 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — WARNING_ACKNOWLEDGEMENT_REQUIRED (StudioError.WARNING_ACKNOWLEDGEMENT_REQUIRED)
|
|
- code: WARNING_ACKNOWLEDGEMENT_REQUIRED
|
|
category: VALIDATION
|
|
http_status: 422
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "경고 확인이 필요합니다. 확인 후 다시 시도해 주세요"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — IDEMPOTENCY_KEY_REUSED (StudioError.IDEMPOTENCY_KEY_REUSED)
|
|
- code: IDEMPOTENCY_KEY_REUSED
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "Idempotency 키가 다른 요청에 재사용되었습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — ASSET_NOT_FOUND (StudioError.ASSET_NOT_FOUND)
|
|
- code: ASSET_NOT_FOUND
|
|
category: NOT_FOUND
|
|
http_status: 404
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "요청한 자산을 찾을 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — ASSET_NOT_READY (StudioError.ASSET_NOT_READY)
|
|
- code: ASSET_NOT_READY
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "자산 처리가 아직 완료되지 않았습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — ASSET_IN_USE (StudioError.ASSET_IN_USE)
|
|
- code: ASSET_IN_USE
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "자산이 사용 중이라 이 작업을 수행할 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — ASSET_QUARANTINED (StudioError.ASSET_QUARANTINED)
|
|
- code: ASSET_QUARANTINED
|
|
category: DATA_INTEGRITY
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: application
|
|
client_safe_message: "자산이 격리 처리되어 사용할 수 없습니다"
|
|
log_level: WARN
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# source: studio-v1.yaml ApiError.code — STUDIO_UNAVAILABLE (StudioError.STUDIO_UNAVAILABLE)
|
|
- code: STUDIO_UNAVAILABLE
|
|
category: TRANSIENT_DEPENDENCY
|
|
http_status: 503
|
|
retryable: true
|
|
retry_after_seconds: 5
|
|
owner_branch: feature-techlog-studio-backend
|
|
owner_layer: infrastructure
|
|
client_safe_message: "Studio 서비스를 일시적으로 사용할 수 없습니다. 잠시 후 다시 시도해 주세요"
|
|
log_level: ERROR
|
|
runbook_link: "runbook://studio/unavailable"
|
|
compatibility_impact: additive
|
|
required_test: StudioErrorTest
|
|
|
|
# === Tech Log Public (feature-techlog-public-v1) ===
|
|
#
|
|
# public-v1.yaml 의 ApiError.code 는 세 값이다. 나머지 하나 INTERNAL_ERROR 는 스켈레톤
|
|
# 공통 코드로 이미 이 레지스트리에 있으므로 여기서 다시 선언하지 않는다.
|
|
#
|
|
# Studio 와 이름을 겹치지 않게 한 이유: 이 레지스트리는 코드 하나에 http_status 하나만
|
|
# 담는다. public 의 400 과 studio 의 422 를 같은 이름으로 쓸 수 없다.
|
|
|
|
# source: public-v1.yaml ApiError.code — PUBLIC_REQUEST_INVALID (PublicError.PUBLIC_REQUEST_INVALID)
|
|
- code: PUBLIC_REQUEST_INVALID
|
|
category: VALIDATION
|
|
http_status: 400
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-public-v1
|
|
owner_layer: application
|
|
client_safe_message: "요청 값이 올바르지 않습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: PublicErrorRegistryTest
|
|
|
|
# source: public-v1.yaml ApiError.code — PUBLIC_RESOURCE_NOT_FOUND (PublicError.PUBLIC_RESOURCE_NOT_FOUND)
|
|
- code: PUBLIC_RESOURCE_NOT_FOUND
|
|
category: NOT_FOUND
|
|
http_status: 404
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-public-v1
|
|
owner_layer: application
|
|
client_safe_message: "요청한 자료를 찾을 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: PublicErrorRegistryTest
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TECH LOG STUDIO MANAGEMENT (studio-management-v1.yaml ApiError.code)
|
|
#
|
|
# ManagementError(dev.caskeleton.application.techlog.error.ManagementError)와 1:1.
|
|
# AUTHENTICATION_REQUIRED / STUDIO_ACCESS_DENIED / REQUEST_VALIDATION_FAILED /
|
|
# VERSION_CONFLICT 는 StudioError 에도 있어 행이 이미 존재한다 — 이 파일의 identity
|
|
# column 은 `code` 이므로 중복 행을 만들지 않는다.
|
|
# ---------------------------------------------------------------------------
|
|
# source: studio-management-v1.yaml ApiError.code — TOPIC_NOT_FOUND (ManagementError.TOPIC_NOT_FOUND)
|
|
- code: TOPIC_NOT_FOUND
|
|
category: NOT_FOUND
|
|
http_status: 404
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "주제를 찾을 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — TOPIC_NAME_TAKEN (ManagementError.TOPIC_NAME_TAKEN)
|
|
- code: TOPIC_NAME_TAKEN
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "같은 이름의 주제가 이미 있습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — TOPIC_SLUG_TAKEN (ManagementError.TOPIC_SLUG_TAKEN)
|
|
- code: TOPIC_SLUG_TAKEN
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "같은 slug 의 주제가 이미 있습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — TOPIC_IN_USE (ManagementError.TOPIC_IN_USE)
|
|
- code: TOPIC_IN_USE
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "이 주제를 쓰는 기록이 있어 삭제할 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — PROJECT_NOT_FOUND (ManagementError.PROJECT_NOT_FOUND)
|
|
- code: PROJECT_NOT_FOUND
|
|
category: NOT_FOUND
|
|
http_status: 404
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "프로젝트를 찾을 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — PROJECT_SLUG_TAKEN (ManagementError.PROJECT_SLUG_TAKEN)
|
|
- code: PROJECT_SLUG_TAKEN
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "같은 slug 의 프로젝트가 이미 있습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — PROJECT_IN_USE (ManagementError.PROJECT_IN_USE)
|
|
- code: PROJECT_IN_USE
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "이 프로젝트에 연결된 기록이 있어 삭제할 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — RELEASE_NOT_FOUND (ManagementError.RELEASE_NOT_FOUND)
|
|
- code: RELEASE_NOT_FOUND
|
|
category: NOT_FOUND
|
|
http_status: 404
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "요청한 릴리즈를 찾을 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — RELEASE_VERSION_TAKEN (ManagementError.RELEASE_VERSION_TAKEN)
|
|
- code: RELEASE_VERSION_TAKEN
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "같은 버전의 릴리즈가 이미 있습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — RELEASE_NOT_PUBLISHABLE (ManagementError.RELEASE_NOT_PUBLISHABLE)
|
|
- code: RELEASE_NOT_PUBLISHABLE
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "지금 상태에서는 발행할 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — DOCUMENT_NOT_FOUND (ManagementError.DOCUMENT_NOT_FOUND)
|
|
- code: DOCUMENT_NOT_FOUND
|
|
category: NOT_FOUND
|
|
http_status: 404
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "작업본을 찾을 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — DOCUMENT_PUBLISHED (ManagementError.DOCUMENT_PUBLISHED)
|
|
- code: DOCUMENT_PUBLISHED
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "공개된 기록은 삭제할 수 없습니다. 먼저 공개를 취소해 주세요"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — QUESTION_NOT_FOUND (ManagementError.QUESTION_NOT_FOUND)
|
|
- code: QUESTION_NOT_FOUND
|
|
category: NOT_FOUND
|
|
http_status: 404
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "질문을 찾을 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — DOCUMENT_IN_USE (ManagementError.DOCUMENT_IN_USE)
|
|
- code: DOCUMENT_IN_USE
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "이 기록을 참조하는 곳이 있어 삭제할 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — QUESTION_IN_USE (ManagementError.QUESTION_IN_USE)
|
|
- code: QUESTION_IN_USE
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "이 질문을 참조하는 곳이 있어 삭제할 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — DECISION_NOT_FOUND (ManagementError.DECISION_NOT_FOUND)
|
|
- code: DECISION_NOT_FOUND
|
|
category: NOT_FOUND
|
|
http_status: 404
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "결정을 찾을 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest
|
|
# source: studio-management-v1.yaml ApiError.code — DECISION_IN_USE (ManagementError.DECISION_IN_USE)
|
|
- code: DECISION_IN_USE
|
|
category: CONFLICT
|
|
http_status: 409
|
|
retryable: false
|
|
retry_after_seconds: null
|
|
owner_branch: feature-techlog-management-v1
|
|
owner_layer: application
|
|
client_safe_message: "이 결정을 참조하는 곳이 있어 삭제할 수 없습니다"
|
|
log_level: INFO
|
|
runbook_link: null
|
|
compatibility_impact: additive
|
|
required_test: ManagementErrorRegistryTest |