One place for hosting & domains

      January 2022

      7 Powerful Ways to Grow Your Email List


      If you’ve just started a small online business, you may already know that modern marketing isn’t just about social media. Email marketing campaigns can play a key role in your overall strategy. However, it’s hard to make much progress if your email list isn’t very long.

      Fortunately, there are lots of simple and inexpensive ways to grow your email list. All this requires is your time, dedication, and a few simple strategies.

      In this post, we’ll explore why email marketing is a great way to grow your business. Then we’ll cover seven powerful ways to grow your email list. Let’s get started!

      Why Email Marketing Is an Effective Way to Grow Your Business

      To succeed, you have to cover all your bases. That’s why any ambitious content marketing strategy should be comprehensive. Some people may forget about email marketing because we spend so much of our time using newer forms of communication, such as social media and messaging platforms.

      However, 4 billion people still use email worldwide. Additionally, the number of emails people send on a daily basis continues to grow every year. Therefore, email marketing remains a highly effective form of lead generation, even in 2022.

      Email is also highly targeted, and when you’re organizing your inbox, you have to address every single message. When there’s such a big audience at stake, neglecting this platform would mean missing out on a significant opportunity.

      The Mailchimp marketing platform’s home page.

      With the rise of newsletters and the continued importance of email, there are now plenty of popular tools like ConvertKit and Mailchimp to help you manage your campaigns.

      These tools offer an all-in-one solution so you can effectively reach your audience. They’re a great place to start – however, you’ll need more than just a dedicated tool. Let’s dive in.

      7 Powerful Ways to Grow Your Email List

      Your email marketing campaigns are only as strong as your email list. Let’s cover some worthwhile ways that you can get more leads and grow your base of subscribers.

      1. Add an Opt-In Form to Your Website

      An opt-in form is a simple element that prompts website visitors to sign-up to receive emails from your business. Usually, an email address is the only personal information requested. This is an easy way to convert readers to subscribers fast, and it’s at the top of our list because it requires minimal effort on your end.

      Reebok’s opt-in form

      Some opt-in forms are more subtle, and others are more attention-grabbing. A few common types include popup forms, exit-intent forms, and bar forms. It’s important to choose your design carefully, and base it on your product and your overall marketing style.

      The timing and presentation of these forms are essential if you want to maximize sign-ups. If you’re not sure where to start, try using opt-in forms on your most popular pages:

      If you’re using WordPress, you can easily create and manage opt-in forms by downloading and installing a plugin like OptinMonster or Convert Pro. Either one will enable you to choose from a wide variety of opt-in form templates and designs, which you can further customize to meet your specific needs.

      Hey look, it’s one of those email opt-in forms!

      Click below to sign up for more how-to’s and tutorials just like this one, delivered to your inbox.

      marketing tips

      2. Host a Giveaway

      There’s nothing like a giveaway to get people excited. People will always love free stuff, so it’s a great way to incentivize readers and followers to sign-up for your email list. If you’re lucky, most won’t even think twice about offering up their email address. When done well, you can see massive growth in your email list in a very short time.

      You can offer up your own products, but if this would cause undue stress on your product pipeline, then you don’t necessarily have to. Another option is to conduct a sponsored event with one or even multiple brands:

      An Instagram holiday giveaway

      For example, a farm-to-table restaurant may partner with a variety of brands or supermarkets that offer organic goods. A small jewelry business might pair with a high-end sunglasses company.

      Of course, be mindful of your partnerships. They must make sense for your business and offer quality products. If you accept a sponsorship deal with a subpar or irrelevant product, this might be off-putting to your audience.

      While you should always be cautious, giveaways can be a gold mine. They are an especially useful opportunity for anyone who already has a strong social media presence:

      If you’re not sure how to run an online giveaway, you can use a free WordPress plugin like RafflePress:

      The RafflePress WordPress plugin

      This solution offers an intuitive giveaway builder that lets you start by choosing a template for your giveaway. Then, you can take advantage of promotional and marketing tools to maximize the number of participants.

      3. Offer Content Upgrades

      Content upgrades are another type of ‘freebie’ you can offer. There are plenty of different types, but some common options are ebooks, workbooks, printables, and guides:

      Free content upgrade: a workbook when you sign up for the email list

      It’s important to remember that these content bonuses should be exclusive. The only way to get them should be by signing up for your email list.

      As with any other strategy you use, it’s best to opt for something that’s relevant to your brand. Here, a food influencer with a cookbook offers recipe box functionality, but also a free recipe download upon sign up:

      A free content upgrade on a recipe website

      Notice how this offer is paired with catchy and irresistible copywriting: “5 Secrets to Super Simple Meals”.

      Don’t forget that you can always combine the strategies on this list to increase their effectiveness. As in the example above, you may consider offering your site’s visitors an opt-in form upon entry, but also dangle a freebie in the navigation bar. The more opportunities visitors have to sign up, the more likely they are to do so.

      4. Organize a Webinar

      A webinar is a workshop, seminar, or presentation that you conduct online using video conferencing software. Most of us have attended one at some point, but if you haven’t, you can still host one without much difficulty.

      For most, a webinar means moving your content to a different medium. It’s cheap and easy, but production may take a good amount of time:

      A webinar sign-up tweet

      This method is well suited to bloggers and influencers who have already monetized their ideas, but any expertise you have to offer will do. If you’re wondering how to host a webinar, you should start by choosing a topic. Then, be sure to nail down the logistical details, spend plenty of time preparing, and of course, promote your webinar.

      This is a great way to get lots of new email addresses, but you want to be thorough so that all your hard work doesn’t go to waste. Remember to make the most of the opportunity. If you host your webinar on a popular social media platform such as Instagram or YouTube, you can boost your engagement as well.

      Additionally, a webinar can provide you with social credibility. Since the time commitment from viewers is significant, this is not only a great way to get emails, but it may potentially lead to long-lasting and loyal customers.

      5. Create an Email Course

      An email course is similar to a traditional online class, but it arrives in your inbox. Typically, an email course is automated and has a short time frame of a few days or a few weeks. It should be easily digestible for participants:

      A five-day email course advertised in an Instagram post

      Email courses are usually self-paced, but individual classes arrive on a set schedule. They don’t require any follow-up or feedback from the instructor. So you don’t have to worry about grading your students. This is also a great way to repurpose old content.

      For example, if you already have a thorough how-to guide on your blog, you can simply expand on that and make it a three-day email course. If you’re using an email marketing service, many of them will already have this process streamlined for you:

      The Mailchimp email course workflow

      The beauty of this type, of course, is that you can re-use it or revamp it whenever your email list needs a boost. You can even promote your products in the course! As always, however, you should make sure that the content of the course is truly useful to the participants.

      6. Give a Welcome Discount

      Another way to get people on your email list is by offering a generous one-time discount. This can be especially effective because it also incentivizes users to buy your product right away:

      The Reebok sign-up form offering a discount

      This can be a more costly strategy than most of the methods on this list. However, if your budget allows for it, you can be sure that many customers will find this offer difficult to resist.

      Typically, an online store will offer somewhere between 10 and 25 percent off of an initial purchase when you sign up for its email list. When you use an eCommerce solution such as WooCommerce, creating discount codes is a breeze.

      7. Start an Email Newsletter

      In the past few years, email newsletters have become wildly popular. They are emerging as a new form of media consumption, but also as a new form of marketing. When you’re producing a quality newsletter, you’re sure to get more email addresses for your list:

      A newsletter sign-up form

      Many newsletters are some combination of quality long-form content blended with product promotion via recommendations and favorites lists. They can also be combined well with affiliate marketing.

      The beauty of the email newsletter is that anyone can do it, from the biggest corporations to the humblest solopreneurs. The only catch is that it’s a long-term obligation. Newsletters typically go out weekly or monthly, so you would need to commit to a regular schedule.

      The majority of the popular email marketing services can offer you step-by-step guidance on how to create a newsletter. However, before you embark on this journey, you should make sure you have a clear goal, and always prioritize quality over quantity.

      Grow Your Business with Email Marketing

      We know that getting a respectable email list together may seem challenging. No one wants to launch an email marketing campaign with just a few people to contact.

      Fortunately, you can make sure that all your hard work gets the viewers it deserves. Just remember to focus on growing your email list with the following strategies before you launch:

      1. Add opt-in forms. Encourage website visitors to sign up for your email list on popular pages, using tools like OptinMonster or Convert Pro.
      2. Host a giveaway. Offer free products or sponsored products to incentivize people to share their email addresses. WordPress plugins like RafflePress make this simple.
      3. Offer content upgrades. Give people quality exclusive content in the form of ebooks, printables, guides, and so much more. You can pair this with opt-in forms to maximize sign-ups.
      4. Organize a webinar. Use your expertise to host a webinar and offer participants valuable knowledge.
      5. Create an email course. Repurpose old pillar content and provide your audience with a practical, short-term course. Use a popular tool like Mailchimp (or any email marketing service) to streamline the process.
      6. Give a discount. Offer people a generous one-time discount for users who sign up for your email list. You can create discount codes in an eCommerce platform such as WooCommerce.
      7. Start an email newsletter. Commit for the long haul and lure users with the promise of quality, regular content. Your preferred email marketing platform should be able to help you get started.

      Get Professional Email @yourdomain

      Promote your website with every message you send when you set up professional email that matches your domain with DreamHost. Plans start at $1.67/mo.

      professional business email



      Source link

      7 Tools That Can Help Boost Your Social Media Engagement


      With so many people plugged into social media, you probably already know that you can’t avoid building up your business’s presence on these platforms. However, it’s about more than just showing up – you’ll also need to find a way to engage with your followers.

      Fortunately, you don’t have to do it alone. By wielding a few powerful social media management tools, you can start interacting with your audience more than ever before.

      In this article, we’ll cover a few reasons why boosting social media engagement is a worthwhile goal. Then, we’ll show you seven tools that can help you achieve it. Let’s get started!

      The Importance of Social Media Engagement

      The very nature of social media is an interactive one. A recent study found that up to 56 percent of people reported recently liking posts from others. As such, these platforms present a unique opportunity to connect with your customers on a personal level.

      Forging this connection has several benefits. For starters, social engagement metrics can provide valuable insights. If you can identify the demographics that are most frequently commenting, liking, and sharing your posts, you can use that data to adjust your target audience. It’s also an efficient way to find potential influencers.

      Moreover, it can give you an idea of how people view your brand. Unfortunately, not all engagement will be positive – you might find comments from disgruntled customers from time to time. However, these can also help you learn about areas where your audience thinks you could improve.

      Engagement is about visibility, too. People who share your content are by extension spreading the word about your brand. This could lead to increased conversions or more widespread awareness of your company.

      Many social media platforms such as Twitter and TikTok use algorithms to determine what users see on their timelines. Thus, boosting your engagement could help you reach more people.

      7 Tools That Can Help Boost Your Social Media Engagement

      You may know that social media engagement is important, but knowing exactly how to improve your brand’s performance in this area can be tricky. Here are seven effective tools to help you get started.

      1. Canva

      The interface for the media-editing program Canva.

      Images are an essential part of your social media presence. Therefore, you’ll want to make sure to take high-quality photos and optimize their effectiveness through careful editing. If you’re looking for an intuitive tool that helps you design stunning images, consider using Canva.

      This tool is not only easy to use, but it also offers a wealth of features to help support your media creation, including:

      • Built-in templates geared towards the best layouts for individual platforms
      • A cohesive Brand Kit feature to keep your posts consistent with colors, fonts, and brand logos
      • Seamless collaboration features for small or large teams

      Stunning designs are more likely to catch your users’ eyes. More importantly, they can help you boost engagement. This is especially true if you use this opportunity to include exciting Calls To Action (CTA).

      If you’re looking to get the most out of Canva, we recommend that you use your designs across all of your platforms. This way, you’re providing a more seamless transition into new graphics for your customers. This can also contribute to consistency in your branding.

      Pricing: Canva offers a powerful free version. If you want all the features it has to offer, you can start with the Pro plan at $119.99 per year. You can also request an enterprise-level quote if you’re running a large team.

      2. Revive Old Posts

      A sample dashboard for Revive Old Posts.

      Content creation can be a challenging process. Sometimes it’s because you’re trying to keep up with the latest trends, but other times it’s because you’re struggling to produce enough posts to keep your page updated. If you want to alleviate some of this pressure, Revive Old Posts is definitely worth your consideration.

      As you can probably guess, this tool can help you make the most of your old content, by ensuring that it gets all the exposure that it deserves. With the help of this plugin, you can give your audience more opportunities to engage.

      Here are some impressive features offered by Revive Old Posts:

      • Automatic addition of optimized hashtags
      • The option to share posts instantly when you publish them on your website
      • Works for pages, posts, media, and custom posts to encompass nearly all types of content

      This plugin can also help you free up some time. Without the need to constantly update your social media pages, you can focus on other areas of your business. Alternatively, you might just find that you can polish your new content even more.

      You might want to consider using Revive Old Posts selectively, based on the platform. For example, if you know that your photo posts perform better on Instagram, you can focus on recycling that content for that specific account.

      Pricing: Plans for Revive Old Posts begin at $75 for a single site. This will give you five feeds and up to 50 shared accounts.

      3. CoSchedule

      A sample calendar made using CoSchedule.

      When it comes to social media, scheduling can be a big deal. After all, an abandoned or even slow-to-update profile doesn’t attract customers. That’s why a tool like CoSchedule can help.

      CoSchedule is a scheduling software made specifically for marketers. It can help organize your posting calendar with color-coded posts. Whether you’re running a platform-wide campaign or just hoping to polish your email strategy, this program can centralize your work and make sure you always have content planned.

      However, it’s a lot more than just a simple calendar. Here are a few other ways that CoSchedule can help you boost your engagement metrics:

      • Social publishing automation based on your custom plan
      • The ability to organize calendars around different teams or platforms
      • Stores assets and files to ensure that you include robust content for every post

      Consistent posting is key to increased engagement. Not only can CoSchedule help you keep this up, but it can also take some of the work off your plate by automating the process. You can also collaborate with other creators.

      If you choose to use CoSchedule, we highly recommend that you make use of the content progress function. This way, you’ll always know if you’re ready to publish a new post. This can be especially useful for large-scale marketing efforts.

      Pricing: For $29 per person, per month, you can gain access to all of CoSchedule’s features. If you’re interested in the full marketing suite, you will need to contact sales for a quote.

      Want more social media tips in your inbox?

      Click below to sign up for more how-to’s and tutorials just like this one, delivered to your inbox.

      marketing tips

      4. Sniply

      The sample interface for Sniply.

      For most social media platforms, you have to contend with a certain word count. This means every character counts. This can be tricky if you’re using long, complicated URLs. If you’re looking to get the most value out of a short post, we recommend using Sniply.

      Sniply is a URL shortener at its core. However, it can also help you streamline a customized link to serve a specific CTA. As such, you can achieve more brand consistency in a much cleaner way.

      In addition, Sniply has a few other impressive functions, such as:

      • The ability to embed URLs in the form of buttons, text, form, or images for the most natural integration
      • Engagement options ranging from links to email list sign-ups
      • Individual tracking metrics to see how each link’s post is performing

      Sniply also makes it easy for users to engage. Complex links can scare people away. However, short, neat URLs may help your audience feel more comfortable sharing your posts.

      Pricing: The basic option begins at $29 per month for up to two brand profiles, one team member, and 5,000 clicks. The Pro plan comes in at $79 per month and gets you six brand profiles, three team members, and 20,000 clicks.

      5. Woorise

      An example of a giveaway made with Woorise.

      If you want your users to engage with your brand, you may need to go beyond traditional CTAs. Providing users with more interactive content such as giveaways can get them more invested – especially if engagement metrics such as likes and shares are part of the entry conditions.

      This is why we recommend Woorise. This tool comes with several functions, but we’re particularly impressed with its giveaway functionality. It offers a ton of ways to create a stellar contest that will get your audience interacting with you, such as:

      • Require combinations of specific engagement activities such as follows and comments for users to enter
      • Customize the sign-up page with everything from the color palette to photos
      • Embed widgets on social media to ensure that all of your audiences have a chance to sign up

      Simply put, giveaways work because users get invested. Not only will this boost your engagement metrics, but it can also encourage them to follow your brand for more opportunities.

      To get the most out of Woorise, consider doing small giveaways regularly. These provide more chances for people to share your content. As a bonus, these can also be a lot easier on your budget.

      Pricing: You can get started with a free version that has limited functionality. As for the paid version, you can start at $23 per month for one site with unlimited campaigns and 2,000 entries per month. If you want to bump that up to 5,000 entries, you can choose the Grow plan at $39 per month.

      6. Social Searcher

      An example of multiple tweets collected by Social Searcher.

      Part of successfully boosting engagement is knowing what your audience is talking about. This doesn’t just apply to trends, either: knowing how your users feel about your brand, in general, is crucial to creating the content that they want to interact with. That’s why we’re fans of Social Searcher.

      Social Searcher is – as the name suggests – a search engine geared towards social media platforms. It can help you monitor what people are saying about you across multiple sites at once. This applies to individual sites or a more generalized report.

      Here are a few stand-out features that make Social Searcher worth your consideration:

      • Tracking of tagged posts, as well as ones that mention your brand
      • Analysis comparing positive mentions against negative ones
      • Alerts about top hashtags in your niche to help your content stay timely

      Generally speaking, users will only interact with posts that are relevant to them. Social Searcher can help you boost engagement by pinpointing these topics and responding accordingly. You might also be able to use it to learn more about your audience.

      Our favorite thing about this tool is the universal dashboard. You can get information on users across several different sites, allowing you to focus on specific campaigns while also understanding the bigger picture.

      Pricing: You can use a limited version of this tool for free. Paid plans start at 3.49 Euros per month (roughly $4.04). They also offer a 14-day free trial, so you can give it a shot before you commit.

      7. Easy Affiliate

      A sample interface for the Easy Affiliate plugin.

      If you aren’t using an affiliate program for your brand, now might be the time to start. Working with influencers can be a natural and affordable way to reach new audiences on social media. Their work on these platforms can in turn lead curious users to your profile for more information.

      However, managing affiliates across different social media accounts can be tough. If you’re looking for a tool to help you out, we recommend Easy Affiliate. This resource enables you to manage, track, and reward the influencers that boost your engagement metrics the most.

      Easy Affiliate uses several powerful features to help you organize your program, including:

      • The opportunity to provide affiliates with full feature links and banners for a consistent brand experience
      • An easy-to-understand dashboard that helps you see your top performers
      • A simple payout system to accurately compensate those who can increase your engagement the most

      Influencers are more than just a way to advertise your site – they can also organically boost brand awareness. Users who trust these affiliates might in turn trust your brand and seek out your content. This can help you increase your engagement and boost your conversion rate.

      Pricing: Let’s take a look at Easy Affiliate’s pricing. The basic plan starts at $99.50. This will give you all the features for a single site, including unlimited affiliates and a full tracking system.

      Getting by With a Little Help From Our Friends

      Social media engagement is essential for modern brands. User interactions can be the difference between a low profile and a huge following. Fortunately, boosting your engagement metrics can be easy with the help of a few key tools.

      In this article, we showed you seven different tools. Some of them can appeal to users directly, while others can provide crucial insights into your audience. By using one – or even a few! – you can start taking full advantage of your brand’s potential.

      Get Social and Grow Your Business with DreamHost

      Our experts will help create a powerful social media strategy and level up your execution so you can focus on running your business.

      social media marketing



      Source link

      How to Add Extra Information to Errors in Go


      The author selected the Diversity in Tech Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      When a function in Go fails, the function will return a value using the error interface to allow the caller to handle that failure. In many cases, developers will use the fmt.Errorf function in the fmt package to return these values. Prior to Go 1.13, though, a downside of using this function is that you would lose information about any errors that may have caused the error to be returned. To solve this, developers would either use packages to provide a way to “wrap” errors inside other errors or create custom errors by implementing the Error() string method on one of their struct error types. Sometimes it can be tedious to create these struct types if you have a number of errors that don’t need to be handled explicitly by the callers, though, so in Go 1.13, the language added features to make it easier to handle these cases.

      One feature is the ability to wrap errors using the fmt.Errorf function with an error value that can be unwrapped later to access the wrapped errors. This builds the error-wrapping functionality into the Go standard library, so there’s no longer any need to use a third-party library.

      Additionally, the functions errors.Is and errors.As make it easier to determine if a specific error is wrapped anywhere inside a given error, and will also give you access to that specific error directly without needing to unwrap all the errors yourself.

      In this tutorial, you’ll create a program that uses these functions to include additional information in errors returned from your functions, and then create your own custom error struct that supports the wrapping and unwrapping functionality.

      Prerequisites

      Returning and Handling Errors in Go

      When an error occurs in a program, it’s good practice to handle those errors so your users never see them — but to handle the errors, you need to know about them first. In Go, you can handle errors in your program by returning information about the error from your functions using a special interface type, the error interface. Using the error interface allows any Go type to be returned as an error value as long as that type has an Error() string method defined. The Go standard library provides functionality to create errors for these return values, such as the fmt.Errorf function.

      In this section, you’ll create a program with a function that uses fmt.Errorf to return an error, and you will also add an error handler to check for the errors that the function could return. (If you’d like more information on handling errors in Go, please see the tutorial, Handling Errors in Go.)

      Many developers have a directory to keep current projects. In this tutorial, you’ll use a directory named projects.

      To begin, make the projects directory and navigate to it:

      • mkdir projects
      • cd projects

      From the projects directory, create a new errtutorial directory to keep the new program in:

      Next, navigate into the new directory with the cd command:

      Once you’re in the errtutorial directory, use the go mod init command to create a new module named errtutorial:

      After creating the Go module, open a file named main.go in the errtutorial directory using nano, or your favorite editor:

      Next, you will write a program. The program will loop over the numbers 1 through 3 and try to determine if those numbers are valid or not using a function called validateValue. If the number is determined to be invalid, the program will use the fmt.Errorf function to generate an error value that is returned from the function. The fmt.Errorf function allows you to create an error value where the error message is the message you provide to the function. It works similarly to fmt.Printf, but instead of printing the message to the screen it returns it as an error instead.

      Then, in the main function, the error value will be checked to see if it’s a nil value or not. If it is a nil value, the function succeeded and the valid! message is printed. If it’s not, the error received is printed instead.

      To begin your program, add the following code into your main.go file:

      projects/errtutorial/main.go

      package main
      
      import (
          "fmt"
      )
      
      func validateValue(number int) error {
          if number == 1 {
              return fmt.Errorf("that's odd")
          } else if number == 2 {
              return fmt.Errorf("uh oh")
          }
          return nil
      }
      
      func main() {
          for num := 1; num <= 3; num++ {
              fmt.Printf("validating %d... ", num)
              err := validateValue(num)
              if err != nil {
                  fmt.Println("there was an error:", err)
              } else {
                  fmt.Println("valid!")
              }
          }
      }
      

      The validateValue function from the program takes a number and then returns an error based on whether it was determined to be a valid value or not. In this program, the number 1 is invalid and returns the error that's odd. The number 2 is invalid and returns the error uh oh. The validateValue function uses the fmt.Errorf function to generate the error value being returned. The fmt.Errorf function is convenient for returning errors because it allows you to format an error message using formatting similar to fmt.Printf or fmt.Sprintf without needing to then pass that string to errors.New.

      In the main function, the for loop will start by iterating over each number from 1 to 3 and will store the value in the num variable. Inside the loop body, a call to fmt.Printf will print the number the program is currently validating. Then, it will call the validateValue function and pass in num, the current number being validated, and store the error result in the err variable. Lastly, if err is not nil it means an error occured during validation and the error message is printed using fmt.Println. The else clause of the error check will print "valid!" when an error wasn’t encountered.

      After saving your changes, run your program using the go run command with main.go as the argument from the errtutorial directory:

      The output from running the program will show that validation was run for each number and number 1 and number 2 returned their appropriate errors:

      Output

      validating 1... there was an error: that's odd validating 2... there was an error: uh oh validating 3... valid!

      When you look at the output from the program, you’ll see the program tried to validate all three numbers. The first time it says the validateValue function returned the that's odd error, which would be expected for the value of 1. The next value, 2, also shows it returned an error, but it was the uh oh error this time . Finally, the 3 value returns nil for the error value, meaning there wasn’t an error and the number is valid. The way the validateValue function is written, the nil error value would be returned for any values that aren’t either 1 or 2.

      In this section, you used fmt.Errorf to create error values you returned from a function. You also added an error handler to print out the error message when any error is returned from the function. At times, though, it can be useful to know what an error means, not just that an error occurred. In the next section, you’ll learn to customize error handling for specific cases.

      Handling Specific Errors Using Sentinel Errors

      When you receive an error value from a function, the most basic error handling is to check if the error value is nil or not. This will tell you if the function had an error, but sometimes you may want to customize error handling for a specific error case. For example, imagine you have code connecting to a remote server, and the only error information you get back is “you had an error”. You may wish to tell whether the error was because the server was unavailable or if your connection credentials were invalid. If you knew the error meant a user’s credentials were wrong, you might want to let the user know right away. But if the error means the server was unavailable, you may want to try reconnecting a few times before letting the user know. Determining the difference between these errors allows you to write more robust and user-friendly programs.

      One way you could check for a particular type of error might be using the Error method on an error type to get the message from the error and compare that value to the type of error you’re looking for. Imagine that in your program, you want to show a message other than there was an error: uh oh when the error value is uh oh. One approach to handling this case would be to check the value returned from the Error method, like so:

      if err.Error() == "uh oh" {
          // Handle 'uh oh' error.
          fmt.Println("oh no!")
      }
      

      Checking the string value of err.Error() to see if it’s the value uh oh, as in the code above, would work in this case. But the code would not work if the uh oh error string is slightly different elsewhere in the program. Checking errors this way can also lead to significant updates to code if the error’s message itself needs to be updated because every place the error is checked would need to be updated. Take the following code, for example:

      func giveMeError() error {
          return fmt.Errorf("uh h")
      }
      
      err := giveMeError()
      if err.Error() == "uh h" {
          // "uh h" error code
      }
      

      In this code, the error message includes a typo and is missing the o in uh oh. If this is noticed and fixed at some point, but only after adding this error checking in several places, all those places will need to have their checks updated to err.Error() == "uh oh". If one is missed, which could be easy because it’s only a single character change, the expected custom error handler will not run because it’s expecting uh h and not uh oh.

      In cases like these, where you may want to handle a specific error differently than others, it’s common to create a variable whose purpose is to hold an error value. This way, the code can check against that variable instead of a string. Typically, these variables begin with either err or Err in their names to signify they’re errors. If the error is only meant to be used within the package it’s defined in, you would want to use the err prefix. If the error is meant to be used elsewhere, you would instead use the Err prefix to make it an exported value, similar to a function or a struct.

      Now, let’s say you were using one of these error values in the typo example from before:

      var errUhOh = fmt.Errorf("uh h")
      
      func giveMeError() error {
          return errUhOh
      }
      
      err := giveMeError()
      if err == errUhOh {
          // "uh oh" error code
      }
      

      In this example, the variable errUhOh is defined as the error value for an “uh oh” error (even though it’s misspelled). The giveMeError function returns the value of errUhOh because it wants to let the caller know that an “uh oh” error happened. Then, the error handling code compares the err value returned from giveMeError against errUhOh to see if an “uh oh” error is the one that happened. Even if the typo is found and fixed, all the code would still be working because the error check is checking against the value of errUhOh, and the value of errUhOh is the fixed version of the error value that giveMeError is returning.

      An error value that is intended to be checked and compared in this way is known as a sentinel error. A sentinel error is an error that’s designed to be a unique value that can always be compared against for a specific meaning. The errUhOh value above will always have the same meaning, that an “uh oh” error occurred, so a program can rely on comparing an error to errUhOh to determine whether that error occurred.

      The Go standard library also defines a number of sentinel errors that are available when developing Go programs. One example would be the sql.ErrNoRows error. The sql.ErrNoRows error is returned when a database query doesn’t return any results, so that error can be handled differently from a connection error. Since it’s a sentinel error, it can be compared against in error-checking code to know when a query doesn’t return any rows, and the program can handle that differently than other errors.

      Generally, when creating a sentinel error value, the errors.New function from the errors package is used instead of the fmt.Errorf function you’ve been using thus far. Using errors.New instead of fmt.Errorf does not make any foundational changes to how the error works, though, and both functions could be used interchangeably most of the time. The biggest difference between the two is the errors.New function will only create an error with a static message and the fmt.Errorf function allows formatting the string with values, similar to fmt.Printf or fmt.Sprintf. Since sentinel errors are fundamental errors with values that don’t change, it’s common to use errors.New to create them.

      Now, update your program to use a sentinel error for the “uh oh” error instead of fmt.Errorf.

      First, open the main.go file to add the new errUhOh sentinel error and update the program to use it. The validateValue function is updated to return the sentinel error instead of using fmt.Errorf. The main function is updated to check for the errUhOh sentinel error and print oh no! when it encounters it instead of the there was an error: message it shows for other errors.

      projects/errtutorial/main.go

      package main
      
      import (
          "errors"
          "fmt"
      )
      
      var (
          errUhOh = errors.New("uh oh")
      )
      
      func validateValue(number int) error {
          if number == 1 {
              return fmt.Errorf("that's odd")
          } else if number == 2 {
              return errUhOh
          }
          return nil
      }
      
      func main() {
          for num := 1; num <= 3; num++ {
              fmt.Printf("validating %d... ", num)
              err := validateValue(num)
              if err == errUhOh {
                  fmt.Println("oh no!")
              } else if err != nil {
                  fmt.Println("there was an error:", err)
              } else {
                  fmt.Println("valid!")
              }
          }
      }
      

      Now, save your code and use go run to run your program again:

      This time the output will show the generic error output for the 1 value, but it uses the custom oh no! message when it sees the errUhOh error returned from validateValue for 2:

      Output

      validating 1... there was an error: that's odd validating 2... oh no! validating 3... valid!

      Using sentinel errors inside your error checking makes it easier to handle special error cases. For example, they can help determine whether the file you’re reading is failing because you’ve reached the end of the file, which is signified by the io.EOF sentinel error, or if it’s failing for some other reason.

      In this section, you created a Go program that uses a sentinel error using errors.New to signify when a specific type of error occurred. Over time as your program grows, though, you may get to the point where you’d like more information included in your error than just the uh oh error value. This error value doesn’t give any context on where the error happened or why it happened, and it can be hard to track down specifics of the error in larger programs. To aid in troubleshooting and to cut down the time for debugging, you can make use of error wrapping to include the specifics you need.

      Wrapping and Unwrapping Errors

      Wrapping errors means taking one error value and putting another error value inside it, like a wrapped gift. Similar to a wrapped gift, though, you need to unwrap it to know what’s inside. Wrapping an error allows you to include additional information about where the error came from or how it happened without losing the original error value, since it’s inside the wrapper.

      Before Go 1.13, it was possible to wrap errors since you could create custom error values that included the original error. But you would either have to create your own wrappers or use a library that already did the work for you. In Go 1.13, though, Go added support for wrapping and unwrapping errors as part of the standard library by adding the errors.Unwrap function and the %w verb for the fmt.Errorf function. In this section, you’ll update your program to use the %w verb to wrap errors with more information, and you’ll then use errors.Unwrap to retrieve the wrapped information.

      Wrapping Errors with fmt.Errorf

      The first feature to examine when wrapping and unwrapping errors is an addition to the existing fmt.Errorf function. In the past, fmt.Errorf was used to create formatted error messages with additional information using verbs such as %s for strings and %v for generic values. Go 1.13 added a new verb with a special case, the %w verb. When the %w verb is included in a format string and an error is provided for the value, the error returned from fmt.Errorf will include the value of the error wrapped in the error being created.

      Now, open the main.go file and update it to include a new function called runValidation. This function will take the number currently being validated and run any validation needed on that number. In this case, it only needs to run the validateValue function. If it encounters an error validating the value it will wrap the error using fmt.Errorf and the %w verb to show there was a run error that occurred, then return that new error. You should also update the main function so instead of calling validateValue directly it calls runValidation instead:

      projects/errtutorial/main.go

      
      ...
      
      var (
          errUhOh = errors.New("uh oh")
      )
      
      func runValidation(number int) error {
          err := validateValue(number)
          if err != nil {
              return fmt.Errorf("run error: %w", err)
          }
          return nil
      }
      
      ...
      
      func main() {
          for num := 1; num <= 3; num++ {
              fmt.Printf("validating %d... ", num)
              err := runValidation(num)
              if err == errUhOh {
                  fmt.Println("oh no!")
              } else if err != nil {
                  fmt.Println("there was an error:", err)
              } else {
                  fmt.Println("valid!")
              }
          }
      }
      

      Once you’ve saved your updates, run the updated program using go run:

      The output will look similar to this:

      Output

      validating 1... there was an error: run error: that's odd validating 2... there was an error: run error: uh oh validating 3... valid!

      There are a few things to look at in this output. First, you’ll see the error message being printed for the value 1 now includes run error: that's odd in the error message. This shows the error was wrapped by runValidation’s fmt.Errorf and that the value of the error being wrapped, that's odd, is included in the error message.

      Next, though, there’s a problem. The special error handling that was added for the errUhOh error isn’t running. If you look at the line validating the 2 input, you’ll see it shows the default error message of there was an error: run error: uh oh instead of the expected oh no! message. You know the validateValue function is still returning the uh oh error because you can see it at the end of the wrapped error, but the error detection of errUhOh is no longer working. This happens because the error being returned by runValidation is no longer errUhOh, it’s the wrapped error created by fmt.Errorf. When the if statement tries to compare the err variable to errUhOh, it returns false because err isn’t equal to errUhOh any more, it’s equal to the error that’s wrapping errUhOh. To fix the errUhOh error checking, you’ll need to retrieve the error from inside the wrapper, using the errors.Unwrap function.

      Unwrapping Errors with errors.Unwrap

      In addition to the %w verb being added in Go 1.13, a few new functions were added to the Go errors package. One of these, the errors.Unwrap function, takes an error as a parameter and, if the error passed in is an error wrapper, it will return the wrapped error. If the error provided isn’t a wrapper, the function will return nil.

      Now, open the main.go file again and, using errors.Unwrap, update the errUhOh error check to handle the case where errUhOh is wrapped inside an error wrapper:

      projects/errtutorial/main.go

      func main() {
          for num := 1; num <= 3; num++ {
              fmt.Printf("validating %d... ", num)
              err := runValidation(num)
              if err == errUhOh || errors.Unwrap(err) == errUhOh {
                  fmt.Println("oh no!")
              } else if err != nil {
                  fmt.Println("there was an error:", err)
              } else {
                  fmt.Println("valid!")
              }
          }
      }
      

      After saving the edits, run the program again:

      The output will look similar to this:

      Output

      validating 1... there was an error: run error: that's odd validating 2... oh no! validating 3... valid!

      Now, in the output, you’ll see the oh no! error handling for the 2 input value is back. The additional errors.Unwrap function call you added to the if statement allows it to detect errUhOh both when err itself is an errUhOh value as well as if err is an error that is directly wrapping errUhOh.

      In this section, you used the %w verb added to fmt.Errorf to wrap the errUhOh error inside another error and give it additional information. Then, you used errors.Unwrap to access the errorUhOh error that is wrapped inside another error. Including errors inside other errors as string values is OK for humans reading error messages, but sometimes you’ll want to include additional information with the error wrapper to aid the program in handling the error, such as the status code in an HTTP request error. When this happens, you can create a new custom error to return.

      Custom Wrapped Errors

      Since Go’s only rule for the error interface is that it includes an Error method, it’s possible to turn many Go types into a custom error. One way is by defining a struct type with extra information about the error, and then also including an Error method.

      For a validation error, it may be useful to know which value actually caused the error. Next, let’s create a new ValueError struct that contains a field for the Value that caused the error and an Err field that contains the actual validation error. Custom error types commonly use the Error suffix on the end of the type name to signify it’s a type that conforms to the error interface.

      Open your main.go file and add the new ValueError error struct, as well as a newValueError function to create instances of the error. You will also need to create a method called Error for ValueError so the struct will be considered an error. This Error method should return the value you want to be displayed whenever the error is converted to a string. In this case, it will use fmt.Sprintf to return a string that shows value error: and then the wrapped error. Also, update the validateValue function so instead of returning just the basic error, it uses the newValueError function to return a custom error:

      projects/errtutorial/main.go

      
      ...
      
      var (
          errUhOh = fmt.Errorf("uh oh")
      )
      
      type ValueError struct {
          Value int
          Err   error
      }
      
      func newValueError(value int, err error) *ValueError {
          return &ValueError{
              Value: value,
              Err:   err,
          }
      }
      
      func (ve *ValueError) Error() string {
          return fmt.Sprintf("value error: %s", ve.Err)
      }
      
      ...
      
      func validateValue(number int) error {
          if number == 1 {
              return newValueError(number, fmt.Errorf("that's odd"))
          } else if number == 2 {
              return newValueError(number, errUhOh)
          }
          return nil
      }
      
      ...
      

      Once your updates are saved, run the program again with go run:

      The output will look similar to this:

      Output

      validating 1... there was an error: run error: value error: that's odd validating 2... there was an error: run error: value error: uh oh validating 3... valid!

      You’ll see that the output now shows the errors are wrapped inside of ValueError by the value error: before them in the output. However, the uh oh error detection is broken again because errUhOh is now inside two layers of wrappers, ValueError and the fmt.Errorf wrapper from runValidation. The code code only uses errors.Unwrap once on the error, so this results in the first errors.Unwrap(err) now only returning a *ValueError and not errUhOh.

      One way to fix this would be to update the errUhOh check to add an additional error check that calls errors.Unwrap() twice to unwrap both layers. To add this, open your main.go file and update your main function to include this change:

      projects/errtutorial/main.go

      
      ...
      
      func main() {
          for num := 1; num <= 3; num++ {
              fmt.Printf("validating %d... ", num)
              err := runValidation(num)
              if err == errUhOh ||
                  errors.Unwrap(err) == errUhOh ||
                  errors.Unwrap(errors.Unwrap(err)) == errUhOh {
                  fmt.Println("oh no!")
              } else if err != nil {
                  fmt.Println("there was an error:", err)
              } else {
                  fmt.Println("valid!")
              }
          }
      }
      

      Now, save your main.go file and use go run to run your program again:

      The output will look similar to this:

      Output

      validating 1... there was an error: run error: value error: that's odd validating 2... there was an error: run error: value error: uh oh validating 3... valid!

      You’ll see that, uh oh, the errUhOh special error handling is still not working. The line validating the 2 input where we’d expect to see the special error handling oh no! output still shows the default there was an error: run error: ... error output. This happens because the errors.Unwrap function doesn’t know how to unwrap the ValueError custom error type. In order for a custom error to be unwrapped, it needs to have its own Unwrap method that returns the inner error as an error value. When creating errors using fmt.Errorf with the %w verb earlier, Go was actually creating an error for you that already has an Unwrap method added, so you didn’t need to do it yourself. Now that you’re using your own custom function, though, you need to add your own.

      To finally fix the errUhOh error case, open main.go and add an Unwrap method to ValueError that returns Err, the field the inner wrapped error is stored in:

      projects/errtutorial/main.go

      
      ...
      
      func (ve *ValueError) Error() string {
          return fmt.Sprintf("value error: %s", ve.Err)
      }
      
      func (ve *ValueError) Unwrap() error {
          return ve.Err
      }
      
      ...
      

      Then, once you’ve saved the new Unwrap method, run your program:

      The output will look similar to this:

      Output

      validating 1... there was an error: run error: value error: that's odd validating 2... oh no! validating 3... valid!

      The output shows the oh no! error handling for the errUhOh error is working again because errors.Unwrap is now able to also unwrap ValueError.

      In this section you created a new, custom ValueError error to provide yourself or your users with information about the validation process as part of the error message. You also added support for error unwrapping to your ValueError so errors.Unwrap can be used to access the wrapped error.

      The error handling is getting a bit clunky and hard to maintain, though. Every time there’s a new layer of wrapping you’ve had to add another errors.Unwrap to the error checking to handle it. Thankfully, the errors.Is and errors.As functions in the errors package are available to make working with wrapped errors easier.

      Working with Wrapped Errors

      If you needed to add a new errors.Unwrap function call for every potential layer of error wrapping your program had, it would get very long and difficult to maintain. For this reason, two additional functions were also added to the errors package in the Go 1.13 release. Both of these functions make it easier to work with errors by allowing you to interact with errors no matter how deeply they’re wrapped inside other errors. The errors.Is function allows you to check if a specific sentinel error value is anywhere inside a wrapped error. The errors.As function allows you to get a reference to a certain type of error anywhere inside a wrapped error.

      Checking an Error Value with errors.Is

      Using errors.Is to check for a specific error makes the errUhOh special error handling much shorter because it handles all the nested error unwrapping you were doing manually. The function takes two error parameters, the first being the error you actually received and the second parameter being the error you want to check against.

      To clean up the errUhOh error handling, open your main.go file and update the errUhOh check in the main function to use errors.Is instead:

      projects/errtutorial/main.go

      
      ...
      
      func main() {
          for num := 1; num <= 3; num++ {
              fmt.Printf("validating %d... ", num)
              err := runValidation(num)
              if errors.Is(err, errUhOh) {
                  fmt.Println("oh no!")
              } else if err != nil {
                  fmt.Println("there was an error:", err)
              } else {
                  fmt.Println("valid!")
              }
          }
      }
      

      Then, save your code and run the program again using go run:

      The output will look similar to this:

      Output

      validating 1... there was an error: run error: value error: that's odd validating 2... oh no! validating 3... valid!

      The output shows the oh no! error message, which means that even though there’s only one error check for errUhOh, it will still be found in the error chain. errors.Is takes advantage of an error type’s Unwrap method to keep digging deeper into a chain of errors until it either finds the error value you’re looking for, a sentinel error, or encounters an Unwrap method that returns a nil value.

      Using errors.Is is the recommended way to check for specific errors now that error wrapping exists as a feature in Go. Not only can it be used for your own error values, but it can also be used for other error values such as the sql.ErrNoRows error mentioned earlier in this tutorial.

      Retrieving an Error Type with errors.As

      The last function added to the errors package in Go 1.13 is the errors.As function. This function is used when you want to get a reference to a certain type of error to interact with it in more detail. For example, the ValueError custom error you added earlier gives access to the actual value being validated in the Value field of the error, but you can only access it if you have a reference to that error first. This is where errors.As comes in. You can give errors.As an error, similar to errors.Is, and a variable for a type of error. It will then go through the error chain to see if any of the wrapped errors match the type provided. If one matches, the variable passed in for the error type will be set with the error errors.As found, and the function will return true. If no error types match, it will return false.

      Using errors.As you can now take advantage of the ValueError type to show additional error information in your error handler. Open your main.go file one last time and update the main function to add a new error handling case for ValueError-type errors that prints out value error, the invalid number, and the validation error:

      projects/errtutorial/main.go

      
      ...
      
      func main() {
          for num := 1; num <= 3; num++ {
              fmt.Printf("validating %d... ", num)
              err := runValidation(num)
      
              var valueErr *ValueError
              if errors.Is(err, errUhOh) {
                  fmt.Println("oh no!")
              } else if errors.As(err, &valueErr) {
                  fmt.Printf("value error (%d): %vn", valueErr.Value, valueErr.Err)
              } else if err != nil {
                  fmt.Println("there was an error:", err)
              } else {
                  fmt.Println("valid!")
              }
          }
      }
      

      In the code above, you declared a new valueErr variable and used errors.As to get a reference to the ValueError if it’s wrapped inside the err value. By getting access to the error as a ValueError, you’re then able to access any additional fields the type provides, such as the actual value that failed validation. This could be helpful if the validation logic happens deeper inside the program and you don’t normally have access to the values to give users hints on where something might have gone wrong. Another example of where this could be helpful is if you’re doing network programming and run into a net.DNSError. By getting a reference to the error, you are able to see if the error was the result of not being able to connect, or if the error was caused by being able to connect, but your resource was not found. Once you know this, you can handle the error in different ways.

      To see errors.As in action, save your file and run the program using go run:

      The output will look similar to this:

      Output

      validating 1... value error (1): that's odd validating 2... oh no! validating 3... valid!

      This time in the output you won’t see the default there was an error: ... message, because all the errors are being handled by other error handlers. The output for validating 1 shows that the errors.As error check returned true because the value error ... error message is being displayed. Since the errors.As function returned true, the valueErr variable is set to be a ValueError and can be used to print out the value that failed validation by accessing valueErr.Value.

      For the 2 value, the output also shows that even though the errUhOh is also wrapped inside a ValueError wrapper, the oh no! special error handler is still executed. This is because the special error handler using errors.Is for errUhOh comes first in the collection of if statements handling the errors. Since this handler returns true before the errors.As even runs, the special oh no! handler is the one executed. If the errors.As in your code came before the errors.Is, the oh no! error message would become the same value error ... as the 1 value, except in this case it would print value error (2): uh oh.

      In this section, you updated your program to use the errors.Is function to remove a lot of additional calls to errors.Unwrap and make your error handling code more robust and future-proof. You also used the errors.As function to check if any of the wrapped errors is a ValueError, and then used fields on the value if one was found.

      Conclusion

      In this tutorial, you wrapped an error using the %w format verb and unwrapped an error using errors.Unwrap. You also created a custom error type that supports errors.Unwrap in your own code. Finally, you used your custom error type to explore the new helper functions errors.Is and errors.As.

      Using these new error functions makes it easier to include deeper information about the errors you create or work with. It also future proofs your code to ensure your error checking continues to work even if errors become deeply nested going forward.

      If you’d like to find more details about how to use the new error features, the Go blog has a post about Working with Errors in Go 1.13. The documentation for the errors package package also includes more information.

      This tutorial is also part of the DigitalOcean How to Code in Go series. The series covers a number of Go topics, from installing Go for the first time to how to use the language itself.



      Source link