Unsure how to define an entity type using Entity API
entity_extract_ids() raises an exception when $info['entity keys']['bundle'] is not defined, or is an empty string. If you don't want to explicitly set the bundle property, you need to set it to  value that is considered empty from PHP, but it is not an empty string, such as an empty array, or FALSE. In such cases, the bundle property is set to the entity type name.

The following is the code contained in entity_extract_ids(), and which raises the exception you are seeing.

if (!empty($info['entity keys']['bundle'])) {
  // Explicitly fail for malformed entities missing the bundle property.
  if (!isset($entity-&gt;{$info['entity keys']['bundle']}) || $entity-&gt;{$info['entity keys'] ['bundle']} === '') {
    throw new EntityMalformedException(t('Missing bundle property on entity of type @entity_type.', array('@entity_type' =&gt; $entity_type)));
  }
  $bundle = $entity-&gt;{$info['entity keys']['bundle']};
}
else {
  // The entity type provides no bundle key: assume  single bundle, named
  // after the entity type.
  $bundle = $entity_type;
}


Keep in mind that entity_get_info() caches the entity information it gets. If you edit an enabled module, you need to disable it, and re-enable it. This forces Drupal to clean the cache it uses, and call the implementations of hook_entity_info() to get a fresh definition of the entity.

  // Use the advanced drupal_static() pattern, since this is called very often.
  static $drupal_static_fast;
  if (!isset($drupal_static_fast)) {
    $drupal_static_fast['entity_info'] = &amp;drupal_static(__FUNCTION__);
  }
  $entity_info = &amp;$drupal_static_fast['entity_info'];

  // hook_entity_info() includes translated strings, so each language is cached
  // separately.
  $langcode = $language-&gt;language;

  if (empty($entity_info)) {
    if ($cache = cache_get("entity_info:$langcode")) {
      $entity_info = $cache-&gt;data;
    }
    else {
      $entity_info = module_invoke_all('entity_info');
      // Merge in default values.
      foreach ($entity_info as $name =&gt; $data) {
      // …
    }
    // …
  }


Installing a new module has the same effect, as Drupal should clear its cache in that case too. If it doesn't happen, you can always force Drupal to clear its cache with cache_clear_all('entity_info', 'cache', TRUE).