diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a31d44..4667dd6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,10 +13,12 @@ and this project adheres to [Semantic Versioning][semver].
 
 ### Changed
 
+- Response object should contain result OR error field ([#64])
 - Fix handling parameters whose names are reserved by Python ([#56])
 
-[#56]: https://github.com/openlawlibrary/pygls/pull/56
+[#64]: https://github.com/openlawlibrary/pygls/pull/64
 [#61]: https://github.com/openlawlibrary/pygls/pull/61
+[#56]: https://github.com/openlawlibrary/pygls/pull/56
 
 ## [0.7.4] - 03/21/2019
 
diff --git a/pygls/protocol.py b/pygls/protocol.py
index 181132f..d035c8f 100644
--- a/pygls/protocol.py
+++ b/pygls/protocol.py
@@ -156,6 +156,13 @@ class JsonRPCResponseMessage:
         self.result = result
         self.error = error
 
+    def without_none_fields(self):
+        if self.result:
+            del self.error
+        else:
+            del self.result
+        return self
+
 
 class JsonRPCProtocol(asyncio.Protocol):
     """Json RPC protocol implementation using on top of `asyncio.Protocol`.
@@ -386,7 +393,7 @@ class JsonRPCProtocol(asyncio.Protocol):
                                           JsonRPCProtocol.VERSION,
                                           result,
                                           error)
-        self._send_data(response)
+        self._send_data(response.without_none_fields())
 
     def connection_made(self, transport: asyncio.Transport):
         """Method from base class, called when connection is established"""

Response object should contain result OR error field
**Current behavior:**

Both _result_ and _error_ fields are serialized and returned as part of response object to the client.

**Expected behavior:**

From json RPC [specification](https://www.jsonrpc.org/specification):

```
jsonrpc
A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0".

result
This member is REQUIRED on success.
This member MUST NOT exist if there was an error invoking the method.
The value of this member is determined by the method invoked on the Server.

error
This member is REQUIRED on error.
This member MUST NOT exist if there was no error triggered during invocation.
The value for this member MUST be an Object as defined in section 5.1.

id
This member is REQUIRED.
It MUST be the same as the value of the id member in the Request Object.
If there was an error in detecting the id in the Request object (e.g. Parse error/Invalid Request), it MUST be Null.
```
diff --git a/tests/test_protocol.py b/tests/test_protocol.py
index 1b5ed79..3e9cf6f 100644
--- a/tests/test_protocol.py
+++ b/tests/test_protocol.py
@@ -71,7 +71,12 @@ def test_deserialize_message_without_jsonrpc_field__should_return_object():
 
     assert type(result).__name__ == 'Object'
     assert result.random == "data"
-    assert result._1 == "def"
+
+    # namedtuple does not guarantee field order
+    try:
+        assert result._0 == "def"
+    except AttributeError:
+        assert result._1 == "def"
 
 
 def test_deserialize_message_should_return_response_message():
@@ -152,6 +157,24 @@ def test_initialize_should_return_server_capabilities(client_server):
     assert isinstance(server_capabilities, InitializeResult)
 
 
+def test_response_object_fields():
+    # Result field set
+    response = JsonRPCResponseMessage(0, '2.0', 'result', None).without_none_fields()
+
+    assert hasattr(response, 'id')
+    assert hasattr(response, 'jsonrpc')
+    assert hasattr(response, 'result')
+    assert not hasattr(response, 'error')
+
+    # Error field set
+    response = JsonRPCResponseMessage(0, '2.0', None, 'error').without_none_fields()
+
+    assert hasattr(response, 'id')
+    assert hasattr(response, 'jsonrpc')
+    assert hasattr(response, 'error')
+    assert not hasattr(response, 'result')
+
+
 def test_to_lsp_name():
     f_name = 'text_document__did_open'
     name = 'textDocument/didOpen'

pip install -e .[dev]
parse_log_pytest
pytest
pytest
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///croot/attrs_1668696182826/work
bandit==1.5.1
certifi @ file:///croot/certifi_1671487769961/work/certifi
entrypoints==0.3
flake8==3.7.7
flit_core @ file:///opt/conda/conda-bld/flit-core_1644941570762/work/source/flit_core
gitdb==4.0.12
GitPython==3.1.44
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1648562407465/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
mccabe==0.6.1
packaging @ file:///croot/packaging_1671697413597/work
pbr==6.1.1
pluggy @ file:///tmp/build/80754af9/pluggy_1648042572264/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pycodestyle==2.5.0
pyflakes==2.1.1
-e git+https://github.com/openlawlibrary/pygls.git@488c5f271865aedd8314edd0206024430adc466e#egg=pygls
pytest==7.1.2
PyYAML==6.0.1
six==1.17.0
smmap==5.0.2
stevedore==3.5.2
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
typing_extensions @ file:///croot/typing_extensions_1669924550328/work
zipp @ file:///croot/zipp_1672387121353/work

name: pygls
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=22.1.0=py37h06a4308_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - flit-core=3.6.0=pyhd3eb1b0_0
  - importlib-metadata=4.11.3=py37h06a4308_0
  - importlib_metadata=4.11.3=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=22.0=py37h06a4308_0
  - pip=22.3.1=py37h06a4308_0
  - pluggy=1.0.0=py37h06a4308_1
  - py=1.11.0=pyhd3eb1b0_0
  - pytest=7.1.2=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py37h06a4308_0
  - typing_extensions=4.4.0=py37h06a4308_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.11.0=py37h06a4308_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - bandit==1.5.1
      - entrypoints==0.3
      - flake8==3.7.7
      - gitdb==4.0.12
      - gitpython==3.1.44
      - mccabe==0.6.1
      - pbr==6.1.1
      - pycodestyle==2.5.0
      - pyflakes==2.1.1
      - pyyaml==6.0.1
      - six==1.17.0
      - smmap==5.0.2
      - stevedore==3.5.2
prefix: /opt/conda/envs/pygls

tests/test_protocol.py::test_response_object_fields
tests/test_protocol.py::test_deserialize_message_with_reserved_words_should_pass_without_errors tests/test_protocol.py::test_deserialize_message_should_return_notification_message tests/test_protocol.py::test_deserialize_message_without_jsonrpc_field__should_return_object tests/test_protocol.py::test_deserialize_message_should_return_response_message tests/test_protocol.py::test_deserialize_message_should_return_request_message tests/test_protocol.py::test_initialize_without_capabilities_should_raise_error tests/test_protocol.py::test_initialize_without_process_id_should_raise_error tests/test_protocol.py::test_initialize_without_root_uri_should_raise_error tests/test_protocol.py::test_initialize_should_return_server_capabilities tests/test_protocol.py::test_to_lsp_name
pygls/protocol.py CHANGELOG.md
pygls/protocol.py CHANGELOG.md