Error Handling Design
Problem Situation
Actual Occurrence
Section titled “Actual Occurrence”When the user executed the command wrangler d1 create:
--- 2026-01-28T06:19:24.460Z error✘ [ERROR] A database with that name already existsKey Points:
- Exit code: Not 0 (treated as an error)
- HTTP Response: 400 Bad Request
- But actual situation: Resource already exists (normal situation)
Why check “Already Exists” first, regardless of the Exit Code?
Section titled “Why check “Already Exists” first, regardless of the Exit Code?”1. Limitations of Exit Codes
Section titled “1. Limitations of Exit Codes”General Exit Code Meanings
Section titled “General Exit Code Meanings”0: Success!= 0: Failure
However, for the Cloudflare API
Section titled “However, for the Cloudflare API”Case A: Resource creation successful
Exit code: 0Output: "✅ Successfully created DB..."Case B: Resource already exists
Exit code: 1 (또는 다른 non-zero 값)Output: "✘ [ERROR] A database with that name already exists"HTTP: 400 Bad RequestCase C: Actual error (insufficient permissions, network error, etc.)
Exit code: 1Output: "✘ [ERROR] Authentication failed"HTTP: 401 Unauthorized2. “Already Exists” is not an error
Section titled “2. “Already Exists” is not an error”Idempotent Operation
Section titled “Idempotent Operation”Resource creation scripts must be idempotent:
- Safe to execute once
- Produces the same result when executed multiple times
- An already existing resource is a normal state, not an error
Example
Section titled “Example”# 첫 번째 실행./setup-cloudflare-resources.sh# → D1 Database 생성됨 (exit code: 0)
# 두 번째 실행 (같은 리소스)./setup-cloudflare-resources.sh# → "already exists" (exit code: 1)# → 하지만 이것은 에러가 아님! 정상적인 상황Design Principle: “Meaning-Based Handling” vs “Exit Code-Based Handling”
Section titled “Design Principle: “Meaning-Based Handling” vs “Exit Code-Based Handling””❌ Exit Code-based processing (problematic approach)
Section titled “❌ Exit Code-based processing (problematic approach)”D1_OUTPUT=$(npx wrangler d1 create "newsfork-metadata-dev" 2>&1)D1_EXIT_CODE=$?
if [[ $D1_EXIT_CODE -eq 0 ]]; then echo "✅ Created"else echo "❌ Error" # ← "already exists"도 에러로 처리됨!fiIssues:
- “already exists” is also marked as an error
- Appears as an error when it’s actually a normal situation
- Confuses users
✅ Meaning-Based Handling (Correct Approach)
Section titled “✅ Meaning-Based Handling (Correct Approach)”D1_OUTPUT=$(npx wrangler d1 create "newsfork-metadata-dev" 2>&1)D1_EXIT_CODE=$?
# 1. 먼저 출력 내용을 확인 (의미 파악)if echo "$D1_OUTPUT" | grep -qiE "already exists"; then echo "ℹ️ Already exists" # 정상 상황elif [[ $D1_EXIT_CODE -eq 0 ]]; then echo "✅ Created" # 성공else echo "❌ Error" # 실제 에러fiAdvantages:
- “already exists” is treated as a normal situation
- Only actual errors are displayed as errors
- Provides clear information to users
Actual Behavior Comparison
Section titled “Actual Behavior Comparison”Scenario 1: Resource Already Exists
Section titled “Scenario 1: Resource Already Exists”Exit Code-Based Handling
Section titled “Exit Code-Based Handling”# Exit code: 1# Output: "✘ [ERROR] A database with that name already exists"
if [[ $EXIT_CODE -eq 0 ]]; then echo "✅ Created" # 실행 안 됨else echo "❌ Error" # ← 잘못된 메시지!fiResult: ”❌ Error” displayed (user confusion)
Meaning-based handling
Section titled “Meaning-based handling”# Exit code: 1# Output: "✘ [ERROR] A database with that name already exists"
if echo "$OUTPUT" | grep -qi "already exists"; then echo "ℹ️ Already exists" # ← 올바른 메시지!elif [[ $EXIT_CODE -eq 0 ]]; then echo "✅ Created"else echo "❌ Error"fiResult: “ℹ️ Already exists” displayed (clarity)
Scenario 2: When an actual error occurs
Section titled “Scenario 2: When an actual error occurs”Exit Code-based processing
Section titled “Exit Code-based processing”# Exit code: 1# Output: "✘ [ERROR] Authentication failed"
if [[ $EXIT_CODE -eq 0 ]]; then echo "✅ Created"else echo "❌ Error" # ← 올바른 메시지fiResult: ”❌ Error” displayed (correct)
Meaning-based processing
Section titled “Meaning-based processing”# Exit code: 1# Output: "✘ [ERROR] Authentication failed"
if echo "$OUTPUT" | grep -qi "already exists"; then echo "ℹ️ Already exists" # 실행 안 됨elif [[ $EXIT_CODE -eq 0 ]]; then echo "✅ Created" # 실행 안 됨else echo "❌ Error" # ← 올바른 메시지fiResult: ”❌ Error” displayed (correct)
How the Cloudflare API Works
Section titled “How the Cloudflare API Works”HTTP Status Code vs Exit Code
Section titled “HTTP Status Code vs Exit Code”The Cloudflare API uses HTTP status codes:
| Situation | HTTP Status | Exit Code | Meaning |
|---|---|---|---|
| Creation Successful | 200 OK | 0 | ✅ Success |
| Already Exists | 400 Bad Request | 1 | ℹ️ Normal (idempotent) |
| No Permission | 401 Unauthorized | 1 | ❌ Actual Error |
| Network Error | - | 1 | ❌ Actual Error |
Issue: Both “Already Exists” and “Actual Error” return the same Exit Code (1).
Wrangler CLI Behavior
Section titled “Wrangler CLI Behavior”Wrangler converts HTTP status codes to Exit Codes:
// Wrangler 내부 로직 (의사 코드)if (httpStatus === 200) { exitCode = 0; // 성공} else { exitCode = 1; // 모든 에러 (400, 401, 500 등 모두)}Result:
- 400 (already exists) → exit code 1
- 401 (unauthorized) → exit code 1
- 500 (server error) → exit code 1
All return the same exit code, making them indistinguishable!
Solution: Analyze output content
Section titled “Solution: Analyze output content”Exit Code alone is insufficient
Section titled “Exit Code alone is insufficient”Exit Code only indicates “success/failure”.
- Success: exit code 0
- Failure: exit code != 0
But it cannot tell you “the type of failure”:
- Already exists (normal)
- No permission (error)
- Network error (error)
Output analysis is essential
Section titled “Output analysis is essential”You must analyze the output to understand “the meaning of the failure”:
# 출력 내용 분석if echo "$OUTPUT" | grep -qi "already exists"; then # 의미: 리소스가 이미 존재함 (정상)elif echo "$OUTPUT" | grep -qi "authentication"; then # 의미: 인증 실패 (에러)elif echo "$OUTPUT" | grep -qi "network"; then # 의미: 네트워크 오류 (에러)else # 의미: 알 수 없는 에러fiProcessing order in actual code
Section titled “Processing order in actual code”Correct sequence
Section titled “Correct sequence”# 1. 명령 실행OUTPUT=$(command 2>&1)EXIT_CODE=$?
# 2. 출력 내용 분석 (의미 파악) - 우선순위 1if echo "$OUTPUT" | grep -qi "already exists"; then # 정상 상황: 이미 존재 echo "ℹ️ Already exists"
# 3. Exit Code 확인 (의미 파악 실패 시) - 우선순위 2elif [[ $EXIT_CODE -eq 0 ]]; then # 성공 echo "✅ Created"
# 4. 실제 에러 처리 - 우선순위 3else # 실제 에러 echo "❌ Error: $OUTPUT"fiWhy this sequence?
Section titled “Why this sequence?”-
Output content is the most accurate information
- Exit Code is ambiguous (multiple situations share the same code)
- Output content is specific (different messages for each situation)
-
“Already exists” is a special case
- Exit Code is an error, but meaning is normal
- Must check first for correct handling
-
Actual errors are handled last
- All errors except “already exists”
- Exit Code != 0 and output content is an error message
Design Principle Summary
Section titled “Design Principle Summary”1. Semantic-based Handling
Section titled “1. Semantic-based Handling”Principle: Prioritize the meaning of output content over Exit Code
Reason:
- Exit Code is ambiguous
- Output content is concrete
- Provides more useful information to the user
2. Idempotent Operation
Section titled “2. Idempotent Operation”Principle: Safe to execute the same operation multiple times
Reason:
- Safe when re-running scripts
- Prevents duplicate executions in CI/CD
- Prevents user errors
3. Fail-Safe Default
Section titled “3. Fail-Safe Default”Principle: Handle ambiguously in a safe manner
Reason:
- Treating “already exists” as an error confuses users
- Handling it as a normal situation is safe
- Actual errors are clearly indicated
Real-world Example
Section titled “Real-world Example”Case 1: Resource already exists
Section titled “Case 1: Resource already exists”$ npx wrangler d1 create newsfork-metadata-dev✘ [ERROR] A database with that name already existsExit code: 1Handling:
# 출력 내용 확인if echo "$OUTPUT" | grep -qi "already exists"; then echo "ℹ️ Already exists" # ✅ 정상 처리fiResult: “ℹ️ Already exists” (clear to user)
Case 2: Actual error (no permission)
Section titled “Case 2: Actual error (no permission)”$ npx wrangler d1 create newsfork-metadata-dev✘ [ERROR] Authentication failedExit code: 1Handling:
# 출력 내용 확인if echo "$OUTPUT" | grep -qi "already exists"; then # 실행 안 됨elif [[ $EXIT_CODE -eq 0 ]]; then # 실행 안 됨else echo "❌ Error: $OUTPUT" # ✅ 에러 처리fiResult: ”❌ Error: Authentication failed” (clear error)
Case 3: Success
Section titled “Case 3: Success”$ npx wrangler d1 create newsfork-metadata-dev✅ Successfully created DB 'newsfork-metadata-dev'Exit code: 0Processing:
# 출력 내용 확인if echo "$OUTPUT" | grep -qi "already exists"; then # 실행 안 됨elif [[ $EXIT_CODE -eq 0 ]]; then echo "✅ Created" # ✅ 성공 처리fiResult: ”✅ Created” (Clear success)
Conclusion
Section titled “Conclusion”Why check “Already Exists” first, regardless of the Exit Code?
Section titled “Why check “Already Exists” first, regardless of the Exit Code?”-
Exit Code ambiguity
- “already exists” and “actual error” share the same Exit Code
- Indistinguishable
-
Output Content is Precise
- Different messages per situation
- Meaning can be clearly understood
-
“Already Exists” is a Normal Situation
- Part of an Idempotent operation
- Not an error, but an informational message
-
Improved User Experience
- Provides clear messages
- Prevents confusion
Core Principle
Section titled “Core Principle”“Exit Code is for reference; output content is the truth”
Exit Code only indicates success/failure, but output content tells you why it failed.
Date Written: 2026-01-28
Design Principle: Semantic-based Error Handling