diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md
index 49a1957c6..5d6178a20 100644
--- a/juniper/CHANGELOG.md
+++ b/juniper/CHANGELOG.md
@@ -4,6 +4,14 @@
 
 - Support raw identifiers in field and argument names. (#[object] macro)
 
+- Most error types now implement `std::error::Error`:
+  - `GraphQLError`
+  - `LexerError`
+  - `ParseError`
+  - `RuleError`
+
+See [#419](https://github.com/graphql-rust/juniper/pull/419).
+
 ## Breaking Changes
 
 - remove old `graphql_object!` macro, rename `object` proc macro to `graphql_object`
diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs
index 093bb6601..c5209a7f2 100644
--- a/juniper/src/executor/mod.rs
+++ b/juniper/src/executor/mod.rs
@@ -1,4 +1,10 @@
-use std::{borrow::Cow, cmp::Ordering, collections::HashMap, fmt::Display, sync::RwLock};
+use std::{
+    borrow::Cow,
+    cmp::Ordering,
+    collections::HashMap,
+    fmt::{self, Debug, Display},
+    sync::RwLock,
+};
 
 use fnv::FnvHashMap;
 
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index e0c858664..5a1b9d433 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -157,6 +157,7 @@ use crate::{
     parser::{parse_document_source, ParseError, Spanning},
     validation::{validate_input_values, visit_all_rules, ValidatorContext},
 };
+use std::fmt;
 
 pub use crate::{
     ast::{FromInputValue, InputValue, Selection, ToInputValue, Type},
@@ -193,6 +194,26 @@ pub enum GraphQLError<'a> {
     IsSubscription,
 }
 
+impl<'a> fmt::Display for GraphQLError<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            GraphQLError::ParseError(error) => write!(f, "{}", error),
+            GraphQLError::ValidationError(errors) => {
+                for error in errors {
+                    writeln!(f, "{}", error)?;
+                }
+                Ok(())
+            }
+            GraphQLError::NoOperationProvided => write!(f, "No operation provided"),
+            GraphQLError::MultipleOperationsProvided => write!(f, "Multiple operations provided"),
+            GraphQLError::UnknownOperationName => write!(f, "Unknown operation name"),
+            GraphQLError::IsSubscription => write!(f, "Subscription are not currently supported"),
+        }
+    }
+}
+
+impl<'a> std::error::Error for GraphQLError<'a> {}
+
 /// Execute a query in a provided schema
 pub fn execute<'a, S, CtxT, QueryT, MutationT>(
     document_source: &'a str,
diff --git a/juniper/src/parser/lexer.rs b/juniper/src/parser/lexer.rs
index 5d8854e9c..4a18afee4 100644
--- a/juniper/src/parser/lexer.rs
+++ b/juniper/src/parser/lexer.rs
@@ -538,3 +538,5 @@ impl fmt::Display for LexerError {
         }
     }
 }
+
+impl std::error::Error for LexerError {}
diff --git a/juniper/src/parser/parser.rs b/juniper/src/parser/parser.rs
index 095fc1e7e..198c24322 100644
--- a/juniper/src/parser/parser.rs
+++ b/juniper/src/parser/parser.rs
@@ -203,3 +203,5 @@ impl<'a> fmt::Display for ParseError<'a> {
         }
     }
 }
+
+impl<'a> std::error::Error for ParseError<'a> {}
diff --git a/juniper/src/parser/utils.rs b/juniper/src/parser/utils.rs
index d5bd2c915..5ea12817c 100644
--- a/juniper/src/parser/utils.rs
+++ b/juniper/src/parser/utils.rs
@@ -90,6 +90,14 @@ impl<T> Spanning<T> {
     }
 }
 
+impl<T: fmt::Display> fmt::Display for Spanning<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}. At {}", self.item, self.start)
+    }
+}
+
+impl<T: std::error::Error> std::error::Error for Spanning<T> {}
+
 impl SourcePosition {
     #[doc(hidden)]
     pub fn new(index: usize, line: usize, col: usize) -> SourcePosition {
@@ -142,3 +150,9 @@ impl SourcePosition {
         self.col
     }
 }
+
+impl fmt::Display for SourcePosition {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}:{}", self.line, self.col)
+    }
+}
diff --git a/juniper/src/validation/context.rs b/juniper/src/validation/context.rs
index 9498bbdae..9a02ae15e 100644
--- a/juniper/src/validation/context.rs
+++ b/juniper/src/validation/context.rs
@@ -1,4 +1,7 @@
-use std::{collections::HashSet, fmt::Debug};
+use std::{
+    collections::HashSet,
+    fmt::{self, Debug},
+};
 
 use crate::ast::{Definition, Document, Type};
 
@@ -48,6 +51,21 @@ impl RuleError {
     }
 }
 
+impl fmt::Display for RuleError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        // this is fine since all `RuleError`s should have at least one source position
+        let locations = self
+            .locations
+            .iter()
+            .map(|location| format!("{}", location))
+            .collect::<Vec<_>>()
+            .join(", ");
+        write!(f, "{}. At {}", self.message, locations)
+    }
+}
+
+impl std::error::Error for RuleError {}
+
 impl<'a, S: Debug> ValidatorContext<'a, S> {
     #[doc(hidden)]
     pub fn new(schema: &'a SchemaType<S>, document: &Document<'a, S>) -> ValidatorContext<'a, S> {
diff --git a/juniper/src/value/mod.rs b/juniper/src/value/mod.rs
index ab7015abe..6e29b069c 100644
--- a/juniper/src/value/mod.rs
+++ b/juniper/src/value/mod.rs
@@ -2,6 +2,7 @@ use crate::{
     ast::{InputValue, ToInputValue},
     parser::Spanning,
 };
+use std::fmt::{self, Display, Formatter};
 mod object;
 mod scalar;
 
@@ -185,6 +186,46 @@ impl<S: ScalarValue> ToInputValue<S> for Value<S> {
     }
 }
 
+impl<S: ScalarValue> Display for Value<S> {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        match self {
+            Value::Null => write!(f, "null"),
+            Value::Scalar(s) => {
+                if let Some(string) = s.as_string() {
+                    write!(f, "\"{}\"", string)
+                } else {
+                    write!(f, "{}", s)
+                }
+            }
+            Value::List(list) => {
+                write!(f, "[")?;
+                for (idx, item) in list.iter().enumerate() {
+                    write!(f, "{}", item)?;
+                    if idx < list.len() - 1 {
+                        write!(f, ", ")?;
+                    }
+                }
+                write!(f, "]")?;
+
+                Ok(())
+            }
+            Value::Object(obj) => {
+                write!(f, "{{")?;
+                for (idx, (key, value)) in obj.iter().enumerate() {
+                    write!(f, "\"{}\": {}", key, value)?;
+
+                    if idx < obj.field_count() - 1 {
+                        write!(f, ", ")?;
+                    }
+                }
+                write!(f, "}}")?;
+
+                Ok(())
+            }
+        }
+    }
+}
+
 impl<S, T> From<Option<T>> for Value<S>
 where
     S: ScalarValue,
@@ -354,4 +395,82 @@ mod tests {
             )
         );
     }
+
+    #[test]
+    fn display_null() {
+        let s: Value<DefaultScalarValue> = graphql_value!(None);
+        assert_eq!("null", format!("{}", s));
+    }
+
+    #[test]
+    fn display_int() {
+        let s: Value<DefaultScalarValue> = graphql_value!(123);
+        assert_eq!("123", format!("{}", s));
+    }
+
+    #[test]
+    fn display_float() {
+        let s: Value<DefaultScalarValue> = graphql_value!(123.456);
+        assert_eq!("123.456", format!("{}", s));
+    }
+
+    #[test]
+    fn display_string() {
+        let s: Value<DefaultScalarValue> = graphql_value!("foo");
+        assert_eq!("\"foo\"", format!("{}", s));
+    }
+
+    #[test]
+    fn display_bool() {
+        let s: Value<DefaultScalarValue> = graphql_value!(false);
+        assert_eq!("false", format!("{}", s));
+
+        let s: Value<DefaultScalarValue> = graphql_value!(true);
+        assert_eq!("true", format!("{}", s));
+    }
+
+    #[test]
+    fn display_list() {
+        let s: Value<DefaultScalarValue> = graphql_value!([1, None, "foo"]);
+        assert_eq!("[1, null, \"foo\"]", format!("{}", s));
+    }
+
+    #[test]
+    fn display_list_one_element() {
+        let s: Value<DefaultScalarValue> = graphql_value!([1]);
+        assert_eq!("[1]", format!("{}", s));
+    }
+
+    #[test]
+    fn display_list_empty() {
+        let s: Value<DefaultScalarValue> = graphql_value!([]);
+        assert_eq!("[]", format!("{}", s));
+    }
+
+    #[test]
+    fn display_object() {
+        let s: Value<DefaultScalarValue> = graphql_value!({
+            "int": 1,
+            "null": None,
+            "string": "foo",
+        });
+        assert_eq!(
+            r#"{"int": 1, "null": null, "string": "foo"}"#,
+            format!("{}", s)
+        );
+    }
+
+    #[test]
+    fn display_object_one_field() {
+        let s: Value<DefaultScalarValue> = graphql_value!({
+            "int": 1,
+        });
+        assert_eq!(r#"{"int": 1}"#, format!("{}", s));
+    }
+
+    #[test]
+    fn display_object_empty() {
+        let s = Value::<DefaultScalarValue>::object(Object::with_capacity(0));
+        assert_eq!(r#"{}"#, format!("{}", s));
+    }
 }