Skip to content

55 bug error handling improvement#58

Merged
kweonminsung merged 6 commits intodevelopfrom
55-bug-error-handling-improvement
Jan 19, 2026
Merged

55 bug error handling improvement#58
kweonminsung merged 6 commits intodevelopfrom
55-bug-error-handling-improvement

Conversation

@kweonminsung
Copy link
Owner

No description provided.

Copilot AI review requested due to automatic review settings January 19, 2026 17:17
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request improves error handling across the codebase by introducing strongly-typed error enums using the thiserror crate, replacing the previous String-based error handling. The changes span database repositories, API services, controllers, and the RNDC client module.

Changes:

  • Introduced typed error enums (DatabaseError, RndcError, ConfigError, ValidationError, ApiError, BindizrError) with proper error messages
  • Refactored all database repository implementations (SQLite, PostgreSQL, MySQL) to use DatabaseError instead of String errors
  • Updated API service layer to use ApiError with proper HTTP status code mapping
  • Improved RNDC client by removing panic catching and using proper error propagation
  • Updated dependencies (rndc 0.1.3 → 0.1.4, thiserror 2.0.17 added, GitHub actions/checkout v5 → v6)
  • Fixed test helper code to remove unnecessary reference operators and improved floating-point comparison tests
  • Minor code formatting improvements in serializer and CLI modules

Reviewed changes

Copilot reviewed 48 out of 49 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
src/error/mod.rs Top-level BindizrError enum (currently unused)
src/database/error.rs DatabaseError enum with sqlx::Error conversion
src/database/repository/*.rs All repository implementations updated to return DatabaseError
src/rndc/error.rs RndcError enum for RNDC-specific errors
src/rndc/mod.rs Removed panic catching, improved error handling
src/config/error.rs ConfigError enum (currently unused)
src/api/error.rs ApiError enum with HTTP response conversion
src/api/validation.rs ValidationError enum (currently unused)
src/api/service/*.rs Updated to use ApiError, but contains semantic errors in error type selection
src/api/controller/*.rs Simplified error handling using ApiError::into_response()
tests/common/mod.rs Fixed unnecessary reference operators for Copy types
src/config/tests.rs Improved floating-point comparison using epsilon
Cargo.toml Added thiserror dependency, updated rndc version
.github/workflows/*.yml Updated actions/checkout from v5 to v6
renovate.json Added automerge configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 314 to 317
return Err(ApiError::NotFound(format!(
"A record with name '{}' already exists in this zone, so CNAME cannot be used",
update_record_request.name
));
)));
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error type used here is semantically incorrect. When a CNAME validation fails due to existing records, it should return ApiError::BadRequest or a validation error, not ApiError::NotFound. NotFound status should be reserved for cases where a requested resource doesn't exist, not for validation failures. This same issue exists in lines 323-326.

Copilot uses AI. Check for mistakes.
Comment on lines 323 to 326
return Err(ApiError::NotFound(format!(
"A CNAME record with name '{}' already exists in this zone",
update_record_request.name
));
)));
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error type used here is semantically incorrect. When a CNAME validation fails due to existing records, it should return ApiError::BadRequest or a validation error, not ApiError::NotFound. NotFound status should be reserved for cases where a requested resource doesn't exist, not for validation failures.

Copilot uses AI. Check for mistakes.
Comment on lines 42 to 45
Err(ApiError::NotFound(format!(
"Failed to fetch records for zone {}",
id
)))
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error type used here is semantically incorrect. When fetching records from the database fails due to a database error, it should return ApiError::InternalServerError rather than ApiError::NotFound. The NotFound status should be reserved for cases where a specific resource doesn't exist, not for database query failures. This same pattern appears in lines 42-45 and line 55-57.

Copilot uses AI. Check for mistakes.
Err(e) => {
log_error!("Failed to fetch all records: {}", e);
Err("Failed to fetch all records".to_string())
Err(ApiError::BadRequest(
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error type used here is semantically incorrect. A database query failure should return ApiError::InternalServerError rather than ApiError::BadRequest. BadRequest is meant for client errors (invalid input), not server-side database failures.

Suggested change
Err(ApiError::BadRequest(
Err(ApiError::InternalServerError(

Copilot uses AI. Check for mistakes.
Err(e) => {
log_error!("Failed to fetch zone: {}", e);
return Err("Failed to create record".to_string());
return Err(ApiError::BadRequest("Failed to create record".to_string()));
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error type used here is semantically incorrect. A database query failure should return ApiError::InternalServerError rather than ApiError::BadRequest. BadRequest is meant for client errors (invalid input), not server-side database failures.

Suggested change
return Err(ApiError::BadRequest("Failed to create record".to_string()));
return Err(ApiError::InternalServerError("Failed to create record".to_string()));

Copilot uses AI. Check for mistakes.
Comment on lines 171 to 174
return Err(ApiError::NotFound(format!(
"A CNAME record with name '{}' already exists in this zone",
create_record_request.name
));
)));
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error type used here is semantically incorrect. When a CNAME validation fails due to existing records, it should return ApiError::BadRequest or a validation error, not ApiError::NotFound. NotFound status should be reserved for cases where a requested resource doesn't exist, not for validation failures.

Copilot uses AI. Check for mistakes.
Err(e) => {
log_error!("Failed to fetch record: {}", e);
Err("Failed to fetch record".to_string())
Err(ApiError::BadRequest("Failed to fetch record".to_string()))
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error type used here is semantically incorrect. A database query failure should return ApiError::InternalServerError rather than ApiError::BadRequest. BadRequest is meant for client errors (invalid input), not server-side database failures.

Copilot uses AI. Check for mistakes.
Ok(Some(zone)) => zone,
Ok(None) => {
return Err("Zone not found".to_string());
return Err(ApiError::BadRequest("Zone not found".to_string()));
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error type used here is semantically incorrect. A database query failure should return ApiError::InternalServerError rather than ApiError::BadRequest. BadRequest is meant for client errors (invalid input), not server-side database failures. This same issue exists in line 253.

Copilot uses AI. Check for mistakes.
Err(e) => {
log_error!("Failed to fetch record: {}", e);
Err("Failed to fetch record".to_string())
Err(ApiError::BadRequest("Failed to fetch record".to_string()))
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error type used here is semantically incorrect. A database query failure should return ApiError::InternalServerError rather than ApiError::BadRequest. BadRequest is meant for client errors (invalid input), not server-side database failures.

Suggested change
Err(ApiError::BadRequest("Failed to fetch record".to_string()))
Err(ApiError::InternalServerError("Failed to fetch record".to_string()))

Copilot uses AI. Check for mistakes.
Err(e) => {
log_error!("Failed to check existing record: {}", e);
return Err("Failed to update record".to_string());
return Err(ApiError::BadRequest("Failed to update record".to_string()));
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error type used here is semantically incorrect. A database query failure should return ApiError::InternalServerError rather than ApiError::BadRequest. BadRequest is meant for client errors (invalid input), not server-side database failures.

Suggested change
return Err(ApiError::BadRequest("Failed to update record".to_string()));
return Err(ApiError::InternalServerError(
"Failed to update record".to_string(),
));

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 51 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +7 to 20
pub fn get_dns_status() -> Result<String, ApiError> {
let res = match get_rndc_client().command("status") {
Ok(response) => response,
Err(e) => {
log_error!("Failed to get DNS status: {}", e);
return Err("Failed to get DNS status".to_string());
return Err(ApiError::InternalServerError("Failed to get DNS status".to_string()));
}
};

if !res.result {
return Err("Failed to get DNS status".to_string());
return Err(ApiError::InternalServerError("Failed to get DNS status".to_string()));
}

match res.text {
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DNS service methods now return ApiError, but the DNS controller is not updated to use ApiError's IntoResponse implementation. The controller should call err.into_response() instead of manually constructing error responses to maintain consistency with other controllers in the codebase.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 51 out of 52 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +23 to +24
client: rndc::RndcClient::new(&server_url, &algorithm, &secret_key)
.expect("Failed to initialize RNDC client"),
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .expect() call on RNDC client initialization will panic if the client fails to initialize, crashing the application at startup. While this may be intentional for a fail-fast approach, consider whether graceful degradation or a more informative error message would be appropriate. If the RNDC client is required for the application to function, document this requirement clearly.

Copilot uses AI. Check for mistakes.
Comment on lines 1 to 17
use thiserror::Error;

/// Validation-related errors
#[derive(Debug, Error)]
pub enum ValidationError {
#[error("Invalid input: {0}")]
InvalidInput(String),

#[error("Missing required field: {0}")]
MissingField(String),

#[error("Invalid format: {0}")]
InvalidFormat(String),

#[error("Constraint violation: {0}")]
ConstraintViolation(String),
}
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ValidationError enum is defined but not currently used in the codebase. Consider marking it with #[allow(dead_code)] if it's intended for future use, or integrate it into the current validation logic if it should be used now.

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +17
#[error("Connection failed: {0}")]
ConnectionFailed(String),

#[error("Panic occurred: {0}")]
PanicOccurred(String),

#[error("Invalid response: {0}")]
InvalidResponse(String),
}
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RndcError::PanicOccurred, RndcError::ConnectionFailed, and RndcError::InvalidResponse variants are defined but not used anywhere in the codebase. The panic handling was removed from the RNDC client and only CommandFailed is currently used. Consider removing unused variants or marking them with #[allow(dead_code)] if they're intended for future use.

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +26
#[error("Connection failed: {0}")]
ConnectionFailed(String),

#[error("Query failed: {0}")]
QueryFailed(String),

#[error("Record not found: {0}")]
NotFound(String),

#[error("Record already exists: {0}")]
AlreadyExists(String),

#[error("Transaction failed: {0}")]
TransactionFailed(String),

#[error("Migration failed: {0}")]
MigrationFailed(String),

#[error("Pool error: {0}")]
PoolError(String),
}
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several DatabaseError variants are defined but not explicitly used: ConnectionFailed, AlreadyExists, TransactionFailed, and MigrationFailed. While these may be generated automatically through the From<sqlx::Error> implementation, consider whether all these variants are necessary or if some should be marked with #[allow(dead_code)] if they're intended for future use.

Copilot uses AI. Check for mistakes.
"baseBranches": [
"develop"
],
"automerge": true,
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enabling automerge for Renovate will automatically merge dependency updates without manual review. While this can speed up dependency management, it may introduce breaking changes or bugs from updated dependencies. Consider whether this aligns with your project's risk tolerance and testing strategy. You may want to limit automerge to specific types of updates (e.g., only patch versions) using additional configuration.

Suggested change
"automerge": true,
"automerge": false,

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +30
/// Top-level error type
#[derive(Debug, Error)]
pub enum BindizrError {
#[error("Database error: {0}")]
Database(#[from] DatabaseError),

#[error("Configuration error: {0}")]
Config(#[from] ConfigError),

#[error("RNDC error: {0}")]
Rndc(#[from] RndcError),

#[error("Validation error: {0}")]
Validation(#[from] ValidationError),

#[error("Internal error: {0}")]
Internal(String),
}

/// Type alias
pub type Result<T> = std::result::Result<T, BindizrError>;
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The BindizrError type and its Result alias are defined but not currently used in the codebase. While this appears to be laying groundwork for future unified error handling, consider whether these should be marked with #[allow(dead_code)] or if there are immediate plans to integrate them.

Copilot uses AI. Check for mistakes.
Comment on lines 1 to 17
use thiserror::Error;

/// Configuration-related errors
#[derive(Debug, Error)]
pub enum ConfigError {
#[error("Configuration not found: {0}")]
NotFound(String),

#[error("Invalid configuration value: {0}")]
InvalidValue(String),

#[error("Parse error: {0}")]
ParseError(String),

#[error("Missing required configuration: {0}")]
MissingRequired(String),
}
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ConfigError enum is defined but not currently used in the codebase. Consider marking it with #[allow(dead_code)] if it's intended for future use, or integrate it into the current configuration error handling if it should be used now.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 52 out of 53 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 52 out of 53 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@kweonminsung kweonminsung merged commit 9005375 into develop Jan 19, 2026
13 of 14 checks passed
@kweonminsung kweonminsung deleted the 55-bug-error-handling-improvement branch January 19, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant