diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1cf8254..3e93e48 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,15 @@
 
 [Unreleased]: https://github.com/chaostoolkit-incubator/chaostoolkit-aws/compare/0.7.1...HEAD
 
+### Changed
+
+-   make sure instances with no explicit lifecycle can be stopped. They are
+    assumed to be in the `normal` lifecycle as per the last
+    [line of the AWS documentation][]. [#25][25]
+
+[25]: https://github.com/chaostoolkit-incubator/chaostoolkit-aws/issues/25
+[instlifecycledocs]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-purchasing-options.html
+
 ## [0.7.1][]
 
 [0.7.1]: https://github.com/chaostoolkit-incubator/chaostoolkit-aws/compare/0.7.0...0.7.1
diff --git a/chaosaws/ec2/actions.py b/chaosaws/ec2/actions.py
index 230a1a3..c60a05d 100644
--- a/chaosaws/ec2/actions.py
+++ b/chaosaws/ec2/actions.py
@@ -46,7 +46,7 @@ def stop_instance(instance_id: str = None, az: str = None, force: bool = False,
             raise FailedActivity(
                 "No instances in availability zone: {}".format(az))
     else:
-        instance_types = get_instance_type_by_id(instance_id, client)
+        instance_types = get_instance_type_by_id([instance_id], client)
 
     logger.debug(
         "Picked EC2 instance '{}' from AZ '{}' to be stopped".format(
@@ -140,10 +140,17 @@ def get_instance_type_from_response(response: Dict) -> Dict:
 
     for reservation in response['Reservations']:
         for inst in reservation['Instances']:
-            if inst['InstanceLifecycle'] not in instances_type.keys():
+            # when this field is missing, we assume "normal"
+            # which means On-Demand or Reserved
+            # this seems what the last line of the docs imply at
+            # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-purchasing-options.html
+            lifecycle = inst.get('InstanceLifecycle', 'normal')
+
+            if lifecycle not in instances_type.keys():
                 # adding empty list (value) for new instance type (key)
-                instances_type[inst['InstanceLifecycle']] = []
-            instances_type[inst['InstanceLifecycle']].append(
+                instances_type[lifecycle] = []
+
+            instances_type[lifecycle].append(
                 inst['InstanceId'])
 
     return instances_type
@@ -158,7 +165,10 @@ def get_spot_request_ids_from_response(response: Dict) -> List[str]:
 
     for reservation in response['Reservations']:
         for inst in reservation['Instances']:
-            if inst['InstanceLifecycle'] == 'spot':
+            # when this field is missing, we assume "normal"
+            # which means On-Demand or Reserved
+            lifecycle = inst.get('InstanceLifecycle', 'normal')
+            if lifecycle == 'spot':
                 spot_request_ids.append(inst['SpotInstanceRequestId'])
 
     return spot_request_ids

Can't stop instances with explicit lifecycle
When an instance has no explicit lifecycle, the extension fails to stop it. This is usually the case of On-Demand or Reserved instances.
diff --git a/tests/ec2/test_ec2_actions.py b/tests/ec2/test_ec2_actions.py
index 1611aba..37baa7a 100644
--- a/tests/ec2/test_ec2_actions.py
+++ b/tests/ec2/test_ec2_actions.py
@@ -180,3 +180,34 @@ def test_stop_instance_by_specific_filters(aws_client):
     client.describe_instances.assert_called_with(Filters=called_filters)
     client.stop_instances.assert_called_with(
         InstanceIds=[inst_1_id], Force=False)
+
+
+@patch('chaosaws.ec2.actions.aws_client', autospec=True)
+def test_stop_instance_with_no_lifecycle(aws_client):
+    client = MagicMock()
+    aws_client.return_value = client
+    inst_id = "i-1234567890abcdef0"
+    client.describe_instances.return_value = {
+        'Reservations':
+        [{'Instances': [{'InstanceId': inst_id}]}]
+    }
+    stop_instance(inst_id)
+    client.stop_instances.assert_called_with(
+        InstanceIds=[inst_id], Force=False)
+
+
+@patch('chaosaws.ec2.actions.aws_client', autospec=True)
+def test_stop_normal_instance(aws_client):
+    client = MagicMock()
+    aws_client.return_value = client
+    inst_id = "i-1234567890abcdef0"
+    client.describe_instances.return_value = {
+        'Reservations':
+        [{'Instances': [{
+            'InstanceId': inst_id,
+            'InstanceLifecycle': 'normal'
+        }]}]
+    }
+    stop_instance(inst_id)
+    client.stop_instances.assert_called_with(
+        InstanceIds=[inst_id], Force=False)

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-sugar
3.7
requirements.txt requirements-dev.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
aws-requests-auth==0.4.3
aws-xray-sdk==0.95
boto==2.49.0
boto3==1.33.13
botocore==1.33.13
certifi @ file:///croot/certifi_1671487769961/work/certifi
cffi==1.15.1
-e git+https://github.com/chaostoolkit-incubator/chaostoolkit-aws.git@4ab02c19fd02f7c4105f4c5da48f2d70319eeb4d#egg=chaostoolkit_aws
chaostoolkit-lib==1.41.0
charset-normalizer==3.4.1
cookies==2.2.1
coverage==7.2.7
cryptography==44.0.2
docker==6.1.3
ecdsa==0.19.1
exceptiongroup==1.2.2
future==0.18.3
idna==3.10
importlib-metadata==6.7.0
iniconfig==2.0.0
Jinja2==3.1.6
jmespath==1.0.1
jsondiff==1.1.1
jsonpickle==3.4.2
logzero==1.7.0
MarkupSafe==2.1.5
mock==5.2.0
moto==1.3.4
packaging==24.0
pluggy==1.2.0
pyaml==23.5.8
pycodestyle==2.10.0
pycparser==2.21
pycryptodome==3.22.0
pytest==7.4.4
pytest-cov==4.1.0
pytest-sugar==1.0.0
python-dateutil==2.9.0.post0
python-jose==2.0.2
pytz==2025.2
PyYAML==6.0.1
requests==2.31.0
requests-mock==1.12.1
responses==0.23.3
s3transfer==0.8.2
six==1.17.0
termcolor==2.3.0
tomli==2.0.1
types-PyYAML==6.0.12.12
typing_extensions==4.7.1
urllib3==1.26.20
websocket-client==1.6.1
Werkzeug==2.2.3
wrapt==1.16.0
xmltodict==0.14.2
zipp==3.15.0

name: chaostoolkit-aws
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
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_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
  - pip=22.3.1=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
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - aws-requests-auth==0.4.3
      - aws-xray-sdk==0.95
      - boto==2.49.0
      - boto3==1.33.13
      - botocore==1.33.13
      - cffi==1.15.1
      - chaostoolkit-lib==1.41.0
      - charset-normalizer==3.4.1
      - cookies==2.2.1
      - coverage==7.2.7
      - cryptography==44.0.2
      - docker==6.1.3
      - ecdsa==0.19.1
      - exceptiongroup==1.2.2
      - future==0.18.3
      - idna==3.10
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - jinja2==3.1.6
      - jmespath==1.0.1
      - jsondiff==1.1.1
      - jsonpickle==3.4.2
      - logzero==1.7.0
      - markupsafe==2.1.5
      - mock==5.2.0
      - moto==1.3.4
      - packaging==24.0
      - pluggy==1.2.0
      - pyaml==23.5.8
      - pycodestyle==2.10.0
      - pycparser==2.21
      - pycryptodome==3.22.0
      - pytest==7.4.4
      - pytest-cov==4.1.0
      - pytest-sugar==1.0.0
      - python-dateutil==2.9.0.post0
      - python-jose==2.0.2
      - pytz==2025.2
      - pyyaml==6.0.1
      - requests==2.31.0
      - requests-mock==1.12.1
      - responses==0.23.3
      - s3transfer==0.8.2
      - six==1.17.0
      - termcolor==2.3.0
      - tomli==2.0.1
      - types-pyyaml==6.0.12.12
      - typing-extensions==4.7.1
      - urllib3==1.26.20
      - websocket-client==1.6.1
      - werkzeug==2.2.3
      - wrapt==1.16.0
      - xmltodict==0.14.2
      - zipp==3.15.0
prefix: /opt/conda/envs/chaostoolkit-aws

tests/ec2/test_ec2_actions.py::test_stop_instance_with_no_lifecycle
tests/ec2/test_ec2_actions.py::test_stop_instance tests/ec2/test_ec2_actions.py::test_stop_spot_instance tests/ec2/test_ec2_actions.py::test_stop_instance_can_be_forced tests/ec2/test_ec2_actions.py::test_stop_spot_instance_can_be_forced tests/ec2/test_ec2_actions.py::test_stop_instances tests/ec2/test_ec2_actions.py::test_stop_random_instance_in_az tests/ec2/test_ec2_actions.py::test_stop_random_needs_instance_id_or_az tests/ec2/test_ec2_actions.py::test_stop_all_instances_in_az tests/ec2/test_ec2_actions.py::test_stop_all_instances_needs_instance_id_or_az tests/ec2/test_ec2_actions.py::test_stop_all_instances_may_not_have_any_instances tests/ec2/test_ec2_actions.py::test_stop_instance_by_specific_filters tests/ec2/test_ec2_actions.py::test_stop_normal_instance
chaosaws/ec2/actions.py CHANGELOG.md
chaosaws/ec2/actions.py CHANGELOG.md