diff --git a/src/main.rs b/src/main.rs
index 8547f6bd6842f970ee4fd7d64d88a219ae29da5c..fdbfed230fe498b01976b5c20a4ee974659d40d6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -74,7 +74,7 @@ type DbPool = r2d2::Pool<ConnectionManager<PgConnection>>;
 async fn update_database(
     mut job_result_receiver: broadcast::Receiver<JobResult>,
     pool: DbPool,
-) -> Result<(), PostError> {
+) -> Result<(), PageError> {
     loop {
         let job_result = job_result_receiver.recv().await.unwrap();
         if let JobResult {
@@ -285,28 +285,28 @@ async fn main() -> Result<(), Box<dyn Error>> {
 }
 
 #[derive(Error, Debug)]
-#[error("unauthorized")]
-struct UnauthorizedError {}
-
-#[derive(Error, Debug)]
-enum PostError {
+enum PageError {
+    #[error("Unauthorized")]
+    Unauthorized(),
+    #[error("Couldn't render: {0}")]
+    Render(#[from] handlebars::RenderError),
     #[error(transparent)]
-    Unauthorized(#[from] UnauthorizedError),
+    SessionGet(#[from] actix_session::SessionGetError),
     #[error("{0}")]
     Custom(String),
     #[error("{0}")]
     Forbidden(String),
     #[error("{0}")]
     Validation(String),
-    #[error("couldn't get connection from pool")]
+    #[error("Couldn't get connection from pool")]
     ConnectionPool(#[from] r2d2::Error),
-    #[error("couldn't hash")]
+    #[error("Couldn't hash")]
     UserHashing(#[from] user::UserHashingError),
     #[error(transparent)]
     Web(#[from] actix_web::Error),
     #[error(transparent)]
     Queue(#[from] async_channel::SendError<Job>),
-    #[error("couldn't fetch result from database")]
+    #[error("Couldn't fetch result from database")]
     Database(#[from] diesel::result::Error),
     #[error("couldn't insert session")]
     SessionInsert(#[from] actix_session::SessionInsertError),
@@ -316,8 +316,6 @@ enum PostError {
     Zip(#[from] zip::result::ZipError),
 }
 
-type PostResult = Result<HttpResponse, PostError>;
-
 fn error_response_and_log(me: &impl actix_web::error::ResponseError) -> HttpResponse {
     error!("{}", me);
     HttpResponse::build(me.status_code())
@@ -325,69 +323,32 @@ fn error_response_and_log(me: &impl actix_web::error::ResponseError) -> HttpResp
         .body(me.to_string())
 }
 
-impl actix_web::error::ResponseError for PostError {
-    fn error_response(&self) -> HttpResponse {
-        error_response_and_log(self)
-    }
-
-    fn status_code(&self) -> StatusCode {
-        match *self {
-            PostError::Unauthorized(_) => StatusCode::UNAUTHORIZED,
-            PostError::Validation(_) => StatusCode::BAD_REQUEST,
-            PostError::Forbidden(_) => StatusCode::FORBIDDEN,
-            PostError::Custom(_)
-            | PostError::SessionInsert(_)
-            | PostError::ConnectionPool(_)
-            | PostError::Web(_)
-            | PostError::Queue(_)
-            | PostError::Database(_)
-            | PostError::Io(_)
-            | PostError::UserHashing(_)
-            | PostError::Zip(_) => StatusCode::INTERNAL_SERVER_ERROR,
-        }
-    }
-}
-
-#[derive(Error, Debug)]
-enum GetError {
-    #[error("Unauthorized")]
-    Unauthorized(#[from] UnauthorizedError),
-    #[error("{0}")]
-    Forbidden(String),
-    #[error("Couldn't render: {0}")]
-    Render(#[from] handlebars::RenderError),
-    #[error(transparent)]
-    Actix(#[from] actix_web::Error),
-    #[error(transparent)]
-    SessionGet(#[from] actix_session::SessionGetError),
-    #[error("Couldn't fetch result from database")]
-    Diesel(#[from] diesel::result::Error),
-    #[error("Couldn't get connection from pool")]
-    R2d2Pool(#[from] r2d2::Error),
-    #[error("Couldn't find file")]
-    Io(#[from] std::io::Error),
-}
-
-impl actix_web::error::ResponseError for GetError {
+impl actix_web::error::ResponseError for PageError {
     fn error_response(&self) -> HttpResponse {
         error_response_and_log(self)
     }
 
     fn status_code(&self) -> StatusCode {
         match *self {
-            GetError::Unauthorized(_) => StatusCode::UNAUTHORIZED,
-            GetError::Forbidden(_) => StatusCode::FORBIDDEN,
-            GetError::Io(_) => StatusCode::NOT_FOUND,
-            GetError::Render(_)
-            | GetError::SessionGet(_)
-            | GetError::Actix(_)
-            | GetError::Diesel(_)
-            | GetError::R2d2Pool(_) => StatusCode::INTERNAL_SERVER_ERROR,
+            PageError::Unauthorized() => StatusCode::UNAUTHORIZED,
+            PageError::Validation(_) => StatusCode::BAD_REQUEST,
+            PageError::Forbidden(_) => StatusCode::FORBIDDEN,
+            PageError::Custom(_)
+            | PageError::SessionInsert(_)
+            | PageError::ConnectionPool(_)
+            | PageError::Web(_)
+            | PageError::Render(_)
+            | PageError::Queue(_)
+            | PageError::SessionGet(_)
+            | PageError::Database(_)
+            | PageError::Io(_)
+            | PageError::UserHashing(_)
+            | PageError::Zip(_) => StatusCode::INTERNAL_SERVER_ERROR,
         }
     }
 }
 
-type GetResult = Result<HttpResponse, GetError>;
+type PageResult = Result<HttpResponse, PageError>;
 
 #[derive(Serialize)]
 struct BaseContext {
@@ -425,7 +386,7 @@ fn render<Err: From<RenderError>, Ctx: serde::Serialize>(
 }
 
 #[get("/login")]
-async fn get_login(base: BaseContext, hb: web::Data<Handlebars<'_>>) -> GetResult {
+async fn get_login(base: BaseContext, hb: web::Data<Handlebars<'_>>) -> PageResult {
     #[derive(Serialize)]
     struct Context {
         base: BaseContext,
@@ -434,7 +395,7 @@ async fn get_login(base: BaseContext, hb: web::Data<Handlebars<'_>>) -> GetResul
 }
 
 #[get("/me")]
-async fn get_me(base: BaseContext, identity: Identity, hb: web::Data<Handlebars<'_>>) -> GetResult {
+async fn get_me(base: BaseContext, identity: Identity, hb: web::Data<Handlebars<'_>>) -> PageResult {
     require_identity(&identity)?;
     #[derive(Serialize)]
     struct Context {
@@ -448,7 +409,7 @@ async fn get_problem_by_id_assets(
     identity: Identity,
     pool: web::Data<DbPool>,
     path: web::Path<(i32, String)>,
-) -> Result<NamedFile, GetError> {
+) -> Result<NamedFile, PageError> {
     let logged_user = require_identity(&identity)?;
     let (problem_id, asset_filename) = path.into_inner();
     let mut connection = pool.get()?;
@@ -496,8 +457,8 @@ fn get_identity(identity: &Option<&Identity>) -> Option<LoggedUser> {
     })
 }
 
-fn require_identity(identity: &Identity) -> Result<LoggedUser, UnauthorizedError> {
-    get_identity(&Some(identity)).ok_or(UnauthorizedError {})
+fn require_identity(identity: &Identity) -> Result<LoggedUser, PageError> {
+    get_identity(&Some(identity)).ok_or(PageError::Unauthorized())
 }
 
 fn format_duration(duration: chrono::Duration) -> String {
@@ -567,7 +528,7 @@ async fn get_contest_by_id(
     hb: web::Data<Handlebars<'_>>,
     path: web::Path<(i32,)>,
     tz: web::Data<Tz>,
-) -> GetResult {
+) -> PageResult {
     let logged_user = require_identity(&identity)?;
     let (contest_id,) = path.into_inner();
 
@@ -614,7 +575,7 @@ async fn get_contest_scoreboard_by_id(
     hb: web::Data<Handlebars<'_>>,
     path: web::Path<(i32,)>,
     tz: web::Data<Tz>,
-) -> GetResult {
+) -> PageResult {
     let logged_user = require_identity(&identity)?;
     let (contest_id,) = path.into_inner();
 
@@ -682,14 +643,14 @@ async fn get_contest_scoreboard_by_id(
     )
 }
 
-fn assert_contest_not_started(logged_user: &LoggedUser, contest: &Contest) -> Result<(), GetError> {
+fn assert_contest_not_started(logged_user: &LoggedUser, contest: &Contest) -> Result<(), PageError> {
     if contest
         .start_instant
         .map(|s| s > Local::now().naive_utc())
         .unwrap_or(false)
         && !logged_user.is_admin
     {
-        return Err(GetError::Forbidden(
+        return Err(PageError::Forbidden(
             "Essa competição ainda não começou".into(),
         ));
     }
@@ -706,7 +667,7 @@ async fn get_contest_problem_by_id_label(
     session: Session,
     path: web::Path<(i32, String)>,
     tz: web::Data<Tz>,
-) -> GetResult {
+) -> PageResult {
     let logged_user = require_identity(&identity)?;
     let (contest_id, problem_label) = path.into_inner();
 
@@ -781,7 +742,7 @@ struct LoggedUser {
 }
 
 #[post("/logout")]
-async fn post_logout(identity: Identity) -> PostResult {
+async fn post_logout(identity: Identity) -> PageResult {
     identity.logout();
     Ok(redirect_to_root())
 }
@@ -791,23 +752,23 @@ async fn post_login(
     pool: web::Data<DbPool>,
     form: web::Form<LoginForm>,
     request: HttpRequest,
-) -> PostResult {
+) -> PageResult {
     let mut connection = pool.get()?;
 
     match web::block(move || {
         user::check_matching_password(&mut connection, &form.name, &form.password)
     })
     .await
-    .map_err(|e| PostError::Web(e.into()))?
+    .map_err(|e| PageError::Web(e.into()))?
     .map_err(|e| match e {
-        UserHashingError::Database(e) => PostError::Database(e),
-        UserHashingError::Hash(_) => PostError::Validation("Senha inválida".into()),
+        UserHashingError::Database(e) => PageError::Database(e),
+        UserHashingError::Hash(_) => PageError::Validation("Senha inválida".into()),
     })? {
         PasswordMatched::UserDoesntExist => {
-            Err(PostError::Validation("Usuário inexistente".into()))
+            Err(PageError::Validation("Usuário inexistente".into()))
         }
         PasswordMatched::PasswordDoesntMatch => {
-            Err(PostError::Validation("Senha incorreta".into()))
+            Err(PageError::Validation("Senha incorreta".into()))
         }
         PasswordMatched::PasswordMatches(logged_user) => {
             Identity::login(
@@ -817,9 +778,9 @@ async fn post_login(
                     name: (&logged_user.name).into(),
                     is_admin: logged_user.is_admin,
                 })
-                .map_err(|_| PostError::Custom("Usuário no banco de dados inconsistente".into()))?,
+                .map_err(|_| PageError::Custom("Usuário no banco de dados inconsistente".into()))?,
             )
-            .map_err(|_| PostError::Custom("Impossível fazer login".into()))?;
+            .map_err(|_| PageError::Custom("Impossível fazer login".into()))?;
             Ok(redirect_to_root())
         }
     }
@@ -891,7 +852,7 @@ async fn get_submissions(
     pool: web::Data<DbPool>,
     hb: web::Data<Handlebars<'_>>,
     tz: web::Data<Tz>,
-) -> GetResult {
+) -> PageResult {
     let logged_user = require_identity(&identity)?;
     let mut connection = pool.get()?;
 
@@ -924,7 +885,7 @@ async fn get_submissions_me(
     pool: web::Data<DbPool>,
     hb: web::Data<Handlebars<'_>>,
     tz: web::Data<Tz>,
-) -> GetResult {
+) -> PageResult {
     let logged_user = require_identity(&identity)?;
     let mut connection = pool.get()?;
 
@@ -954,7 +915,7 @@ async fn get_submission(
     hb: web::Data<Handlebars<'_>>,
     path: web::Path<(String,)>,
     tz: web::Data<Tz>,
-) -> GetResult {
+) -> PageResult {
     let logged_user = require_identity(&identity)?;
     let (submission_uuid,) = path.into_inner();
     let mut connection = pool.get()?;
@@ -984,7 +945,7 @@ async fn get_submission(
         submission::get_submission_by_uuid(&mut connection, submission_uuid)?;
 
     if user.id != logged_user.id && !logged_user.is_admin {
-        return Err(GetError::Forbidden(
+        return Err(PageError::Forbidden(
             "Não é possível acessar uma submissão de outro usuário".into(),
         ));
     }
@@ -1020,7 +981,7 @@ async fn get_submissions_me_by_contest_id(
     hb: web::Data<Handlebars<'_>>,
     path: web::Path<(i32,)>,
     tz: web::Data<Tz>,
-) -> GetResult {
+) -> PageResult {
     let logged_user = require_identity(&identity)?;
     let (contest_id,) = path.into_inner();
     let mut connection = pool.get()?;
@@ -1052,7 +1013,7 @@ async fn get_submissions_me_by_contest_id_problem_label(
     hb: web::Data<Handlebars<'_>>,
     path: web::Path<(i32, String)>,
     tz: web::Data<Tz>,
-) -> GetResult {
+) -> PageResult {
     let logged_user = require_identity(&identity)?;
     let mut connection = pool.get()?;
 
@@ -1125,10 +1086,10 @@ async fn change_password(
     form: web::Form<ChangePasswordForm>,
     pool: web::Data<DbPool>,
     request: HttpRequest,
-) -> PostResult {
+) -> PageResult {
     let identity = require_identity(&identity)?;
     if form.new_password != form.new_password_repeat {
-        return Err(PostError::Validation("Senhas são diferentes".into()));
+        return Err(PageError::Validation("Senhas são diferentes".into()));
     }
 
     let mut connection = pool.get()?;
@@ -1163,10 +1124,10 @@ async fn create_user(
     pool: web::Data<DbPool>,
     form: web::Form<CreateUserForm>,
     request: HttpRequest,
-) -> PostResult {
+) -> PageResult {
     let identity = require_identity(&identity)?;
     if !identity.is_admin {
-        return Err(PostError::Forbidden(
+        return Err(PageError::Forbidden(
             "Apenas administradores podem fazer isso".into(),
         ));
     }
@@ -1201,10 +1162,10 @@ async fn impersonate_user(
     pool: web::Data<DbPool>,
     form: web::Form<ImpersonateUserForm>,
     request: HttpRequest,
-) -> PostResult {
+) -> PageResult {
     let my_identity = require_identity(&identity)?;
     if !my_identity.is_admin {
-        return Err(PostError::Forbidden(
+        return Err(PageError::Forbidden(
             "Apenas administradores podem fazer isso".into(),
         ));
     }
@@ -1219,9 +1180,9 @@ async fn impersonate_user(
             name: (&user.name).into(),
             is_admin: user.is_admin,
         })
-        .map_err(|_| PostError::Custom("Usuário no banco de dados inconsistente".into()))?,
+        .map_err(|_| PageError::Custom("Usuário no banco de dados inconsistente".into()))?,
     )
-    .map_err(|_| PostError::Custom("Impossível fazer login".into()))?;
+    .map_err(|_| PageError::Custom("Impossível fazer login".into()))?;
 
     Ok(redirect_to_referer(
         "Personificado com sucesso".into(),
@@ -1238,16 +1199,16 @@ async fn create_submission(
     languages: web::Data<Arc<DashMap<String, Language>>>,
     session: Session,
     request: HttpRequest,
-) -> PostResult {
+) -> PageResult {
     let logged_user = require_identity(&identity)?;
     let mut connection = pool.get()?;
 
     languages
         .get(&form.language)
-        .ok_or(PostError::Validation("Linguagem inexistente".into()))?;
+        .ok_or(PageError::Validation("Linguagem inexistente".into()))?;
 
     if !logged_user.is_admin && form.language != "cpp.17.g++" {
-        return Err(PostError::Validation(
+        return Err(PageError::Validation(
             "Somente é possível submeter em C++".into(),
         ));
     }
@@ -1267,10 +1228,7 @@ async fn create_submission(
 
     let contest =
         contest::get_contest_by_contest_problem_id(&mut connection, form.contest_problem_id)?;
-    assert_contest_not_started(&logged_user, &contest).map_err(|e| match e {
-        GetError::Forbidden(m) => PostError::Forbidden(m),
-        _ => PostError::Custom("Erro desconhecido".into()),
-    })?;
+    assert_contest_not_started(&logged_user, &contest)?;
 
     let metadata =
         problem::get_problem_by_contest_id_metadata(&mut connection, form.contest_problem_id)?;
@@ -1306,12 +1264,12 @@ async fn rejudge_submission(
     job_sender: web::Data<Sender<Job>>,
     request: HttpRequest,
     path: web::Path<(String,)>,
-) -> PostResult {
+) -> PageResult {
     let logged_user = require_identity(&identity)?;
     let mut connection = pool.get()?;
 
     if !logged_user.is_admin {
-        return Err(PostError::Forbidden(
+        return Err(PageError::Forbidden(
             "Apenas administradores podem fazer isso".into(),
         ));
     }
@@ -1401,7 +1359,7 @@ fn get_formatted_contests(
     connection: &mut PgConnection,
     user_id: Option<i32>,
     tz: &Tz,
-) -> Result<Vec<FormattedContest>, GetError> {
+) -> Result<Vec<FormattedContest>, PageError> {
     Ok(match user_id {
         Some(user_id) => contest::get_contests_with_acs(connection, user_id)?
             .iter()
@@ -1421,7 +1379,7 @@ async fn get_main(
     pool: web::Data<DbPool>,
     hb: web::Data<Handlebars<'_>>,
     tz: web::Data<Tz>,
-) -> GetResult {
+) -> PageResult {
     let logged_user = get_identity(&identity.as_ref());
 
     #[derive(Serialize)]
@@ -1452,7 +1410,7 @@ async fn get_contests(
     pool: web::Data<DbPool>,
     hb: web::Data<Handlebars<'_>>,
     tz: web::Data<Tz>,
-) -> GetResult {
+) -> PageResult {
     let logged_user = require_identity(&identity)?;
 
     #[derive(Serialize)]
@@ -1481,10 +1439,10 @@ async fn create_contest(
     job_result_sender: web::Data<broadcast::Sender<JobResult>>,
     tz: web::Data<Tz>,
     request: HttpRequest,
-) -> PostResult {
+) -> PageResult {
     let logged_user = require_identity(&identity)?;
     if !logged_user.is_admin {
-        return Err(PostError::Forbidden(
+        return Err(PageError::Forbidden(
             "Apenas administradores podem fazer isso".into(),
         ));
     }
@@ -1514,16 +1472,16 @@ async fn create_contest(
             let data = chunk.unwrap();
             cursor
                 .write(&data)
-                .map_err(|_| PostError::Validation("Corpo inválido".into()))?;
+                .map_err(|_| PageError::Validation("Corpo inválido".into()))?;
         }
 
         cursor.set_position(0);
 
-        fn parse_field(field: &str, cursor: &mut Cursor<Vec<u8>>) -> Result<String, PostError> {
+        fn parse_field(field: &str, cursor: &mut Cursor<Vec<u8>>) -> Result<String, PageError> {
             let mut value = String::new();
             cursor
                 .read_to_string(&mut value)
-                .map_err(|_| PostError::Validation(format!("Campo {} inválido", field)))?;
+                .map_err(|_| PageError::Validation(format!("Campo {} inválido", field)))?;
             Ok(value)
         }
 
@@ -1548,9 +1506,9 @@ async fn create_contest(
 
     let polygon_zip = form
         .polygon_zip
-        .ok_or(PostError::Validation("Arquivo não informado".into()))?;
+        .ok_or(PageError::Validation("Arquivo não informado".into()))?;
     let imported = import_contest::import_file(polygon_zip)
-        .map_err(|e| PostError::Validation(format!("Não foi possível importar: {}", e)))?;
+        .map_err(|e| PageError::Validation(format!("Não foi possível importar: {}", e)))?;
     let mut connection = pool.get()?;
 
     let contest = if form.name.as_ref().unwrap() != "" {
@@ -1652,10 +1610,10 @@ async fn create_contest(
             std::io::copy(&mut zip.by_name(&name)?, &mut File::create(data_path)?)?;
         }
 
-        fn map_codeforces_language(input: &String) -> Result<String, PostError> {
+        fn map_codeforces_language(input: &String) -> Result<String, PageError> {
             Ok(CODEFORCES_LANGUAGE_TO_JUGHISTO
                 .get(input)
-                .ok_or_else(|| PostError::Validation(format!("Linguagem {} não suportada", input)))?
+                .ok_or_else(|| PageError::Validation(format!("Linguagem {} não suportada", input)))?
                 .into())
         }
 
@@ -1665,7 +1623,7 @@ async fn create_contest(
             .solution
             .iter()
             .find(|s| s.tag == "main")
-            .ok_or(PostError::Validation("No main solution".into()))?
+            .ok_or(PageError::Validation("No main solution".into()))?
             .source;
 
         let problem = problem::upsert_problem(
@@ -1740,11 +1698,11 @@ async fn create_contest(
                 )
                 .await
                 .map_err(|_| {
-                    PostError::Validation("Couldn't use an intermediate program".into())
+                    PageError::Validation("Couldn't use an intermediate program".into())
                 })?;
 
                 if run_stats.result != i32::from(job_result::run_cached::Result::Ok) {
-                    return Err(PostError::Validation(
+                    return Err(PageError::Validation(
                         "Couldn't run an intermediate program".into(),
                     ));
                 }
@@ -1762,9 +1720,9 @@ async fn create_contest(
                 problem.time_limit_ms,
             )
             .await
-            .map_err(|_| PostError::Validation("Couldn't run solution on test".into()))?;
+            .map_err(|_| PageError::Validation("Couldn't run solution on test".into()))?;
             if run_stats.exit_code != 0 {
-                return Err(PostError::Validation(
+                return Err(PageError::Validation(
                     "Couldn't run solution on test".into(),
                 ));
             }
@@ -1786,7 +1744,7 @@ async fn create_contest(
             problem.time_limit_ms,
         )
         .await
-        .map_err(|_| PostError::Validation("Couldn't judge main solution".into()))?;
+        .map_err(|_| PageError::Validation("Couldn't judge main solution".into()))?;
 
         if form.name.as_ref().unwrap() != "" {
             contest::relate_problem(
@@ -1794,7 +1752,7 @@ async fn create_contest(
                 contest::NewContestProblems {
                     label: problem_label
                         .get(&problem_id_without_revision)
-                        .ok_or(PostError::Validation(
+                        .ok_or(PageError::Validation(
                             "Arquivo não contém problemas listados".into(),
                         ))?
                         .to_string()