131
votes

I ran this in debug mode, and I attach an image with the details of the exception. How can I know what went wrong? I was trying to inset data in a table. Can't azure give me more details?

Obs: The storage is on Windows Azure not on my machine. The tables were created, but I get this error when inserting data

enter image description here

// Retrieve the storage account from the connection string.
Microsoft.WindowsAzure.Storage.CloudStorageAccount storageAccount = Microsoft.WindowsAzure.Storage.CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=***;AccountKey=***");

// Create the table client.
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();

// Create the table if it doesn't exist.
CloudTable table = tableClient.GetTableReference("EmployeeOnlineHistory");
table.CreateIfNotExists();

and here is the insert code:

public static void SetStatus(Employee e, bool value)
{
    try
    {
        // Retrieve the storage account from the connection string.
        Microsoft.WindowsAzure.Storage.CloudStorageAccount storageAccount = Microsoft.WindowsAzure.Storage.CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=###;AccountKey=###");

        // Create the table client.
        CloudTableClient tableClient = storageAccount.CreateCloudTableClient();

        // Create the CloudTable object that represents the "people" table.
        CloudTable table = tableClient.GetTableReference("EmployeeOnlineHistory");

        // Create a new customer entity.

        if (value == true)
        {
            EmployeeOnlineHistory empHistory = new EmployeeOnlineHistory(e.Id);
            empHistory.IsOnline = true;
            empHistory.OnlineTimestamp = DateTime.Now;
            TableOperation insertOperation = TableOperation.Insert(empHistory);
            table.Execute(insertOperation);
        }
        else
        {
            TableQuery<EmployeeOnlineHistory> query = new TableQuery<EmployeeOnlineHistory>()
                .Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, e.Id.ToString()));
            EmployeeOnlineHistory entity = table.ExecuteQuery(query).Take(1).FirstOrDefault();

            if ((entity!=null)&&(entity.IsOnline))
            {
                entity.IsOnline = false;
                entity.OfflineTimestamp = DateTime.Now;
                entity.OnlineTime = (entity.OfflineTimestamp - entity.OnlineTimestamp);
                TableOperation updateOperation = TableOperation.Replace(entity);
                table.Execute(updateOperation);
            }
            else
            {
                EmployeeOnlineHistory empHistory = new EmployeeOnlineHistory(e.Id);
                empHistory.IsOnline = false;
                empHistory.OfflineTimestamp = DateTime.Now;
                TableOperation insertOperation = TableOperation.Insert(empHistory);
                table.Execute(insertOperation);
            }
        }
    }
    catch (Exception ex)
    {
        //var details = new System.IO.StreamReader(((Microsoft.WindowsAzure.Storage.StorageException)ex)..Response.GetResponseStream()).ReadToEnd();
        LogFile.Error("EmployeeOnlineHistory.setStatus",ex);
    }
}
22
That picture doesn't really help, aside from confirming error 400. Would "would" help is to show the code you executed which resulted in the bad request: how you set up the storage client, how you set up the table, then went to insert, etc.David Makogon
I copy-pasted the code. Hope this helpsRyan
It would help if you said which of the several cases is failing. Also, you're apparently setting some properties outside of this code (eg, PartitionKey and RowKey) so it would help to know what those are and what they're being set to.Brian Reischl
I noticed that when the Exception is thrown first, there is no "View Detail" option but when you continue debugging and the flow goes back to the caller (where the exception pops up again), you can see the View Detail option. From there you can use @Juha Palomäki 's answer to find the errorraghav710

22 Answers

168
votes

400 Error means there's something wrong with the value of one of your properties. One way to find out is to trace the request/response through Fiddler and see the actual data being sent to Windows Azure Storage.

Taking a wild guess, I'm assuming by taking a quick glance at your code that in your model you have some Date/Time type properties (OfflineTimestamp, OnlineTimestamp) and observed that in certain scenarios one of them is initialized with the default value which is "DateTime.MinValue". Please note that the minimum value allowed for a Date/Time type attribute is Jan 1, 1601 (UTC) in Windows Azure[http://msdn.microsoft.com/en-us/library/windowsazure/dd179338.aspx]. Please see if that's not the case. If that's the case, then you could make them nullable type fields so that they don't get populated with the default values.

Have a look at Juha Palomäki's answer below as well... there sometimes is a slightly more useful message in the exception where he suggests (RequestInformation.ExtendedErrorInformation.ErrorMessage)

140
votes

The StorageException contains also a little bit more detailed information about error.

Check in debugger: StorageException.RequestInformation.ExtendedInformation

enter image description here

59
votes

In my case it was a forward slash in the RowKey.

I also received an 'OutOfRangeInput - One of the request inputs is out of range.' error when trying to add manually through the storage emulator.

Characters Disallowed in Key Fields

The following characters are not allowed in values for the PartitionKey and RowKey properties:

  • The forward slash (/) character
  • The backslash (\) character
  • The number sign (#) character
  • The question mark (?) character
  • Control characters from U+0000 to U+001F, including:
    • The horizontal tab (\t) character
    • The linefeed (\n) character
    • The carriage return (\r) character
    • Control characters from U+007F to U+009F

http://msdn.microsoft.com/en-us/library/dd179338.aspx

I wrote an extension method to handle this for me.

public static string ToAzureKeyString(this string str)
{
    var sb = new StringBuilder();
    foreach (var c in str
        .Where(c => c != '/'
                    && c != '\\'
                    && c != '#'
                    && c != '/'
                    && c != '?'
                    && !char.IsControl(c)))
        sb.Append(c);
    return sb.ToString();
}
6
votes

I faced the same issue but the reason in my case was due to size. After digging into the additional exception properties (RequestInformation.ExtendedErrorInformation), found the reason:

ErrorCode : PropertyValueTooLarge ErrorMessage: The property value exceeds the maximum allowed size (64KB). If the property value is a string, it is UTF-16 encoded and the maximum number of characters should be 32K or less.

5
votes

well, in my case i was trying to do this:

CloudBlobContainer container = blobClient.GetContainerReference("SessionMaterials");
await container.CreateIfNotExistsAsync();

because of ContainerName SessionMaterials (as a habit writing in Pascal Case and Camel Case :D) it was causing 400 bad request. So, I just have to make it sessionmaterials. and it worked.

Hope this helps some one.

PS:- Just check exception http response or use fiddler to capture request and response.

3
votes

in my case : Container name was in capital letter. there are limitations when using chars. enter image description here

3
votes

Sometimes it's because your partitionKey or rowKey is NULL

(it was the case for me)

1
votes

A documentation from MS about all the Table Service Error Codes can be found here

1
votes

I had the same BadRequest(400) Error, at the end I fill manually :

enter image description here

And worked for me. Hope this helps!

0
votes

I also faced same kind of issue. In my case PartitionKey value was not set, so by default PartitionKey value was null, which resulted in Object reference not set to an instance of an object. exception

Check if you are providing the appropriate values for PartitionKey or RowKey, you may face such problem.

0
votes

I fixed my cases and it worked fine

My cases:

  1. Row key is not in correct format (400).
  2. Combination of partitionkey and rowkey is not unique (409).
0
votes

I was getting a 400 Bad Request because I was using ZRS (Zone Redundant Storage), and Analytics isn't available for this type of storage. I wasn't aware I was using Analytics.

I deleted the storage container and recreated as GRS and now it works fine.

0
votes

I was getting a (400) Bad Request, StatusMessage:Bad Request, ErrorCode:OutOfRangeInput when the entity had a property DateTime not set (= DateTime.MinValue)

0
votes

In my case: I included blob metadata with a tag name containing a hyphen.

var blob = container.GetBlockBlobReference(filename);
blob.Metadata.Add("added-by", Environment.UserName);
//.. other metadata
blob.UploadFromStream(filestream);

The dash in "added-by" was the problem, and later RTFM told me that tag names must conform to C# identifier conventions.

Ref: https://docs.microsoft.com/en-us/azure/storage/blobs/storage-properties-metadata

Underscore works fine.

0
votes

In my case, i should not add PartitionKey and Rowkey in my entity class. It should be from the base class. Below would just work.

public class TableRunLogMessage:TableEntity
{
      public string status { get; set; }
      public long logged { get; set; }


      public TableRunLogMessage() { }
}
0
votes

If you're using NodeJS and stumbled across this post, only to find that you don't get that lovely detailed information in your error object; you can utilize a proxy to get those details. However, since no one here mentions HOW to use a proxy.

The simplest way with NodeJS is by setting two environmental variables:

NODE_TLS_REJECT_UNAUTHORIZED=0
This disables SSL checks so you can intercept your own SSL requests. This leaves you open to Man-in-The-Middle attacks and should NEVER make it to production, and I wouldn't even leave it in development for long. However, it will allow you to intercept the HTTP Requests.

HTTP_PROXY=http://127.0.0.1:8888
This sets node to utilize a proxy listening on your localhost at port 8888. Port 8888 is the default for Fiddler. Many other proxies default to 8080.

If you're actually utilizing C#, like the author of this post is doing; you can simply install Fiddler and set it to intercept. By default it should intercept the requests. You may need to also trust Fiddler's certificate or otherwise do the equivalent of Node's "NODE_TLS_REJECT_UNAUTHORIZED=0".

0
votes

I got 400-BadRequest response from Azure Storage Account Table API. Exception information showed that "The account being accessed does not support http.". I figured that we must use https in the connection string when "Secure transfer required" is enabled in storage account configuration as shown in the below image.enter image description here

0
votes

In my case for create new instalnce of "TableBotDataStore" class (MS bot framework) we pass "tableName" parameter with hyphen like "master-bot" and TableBotDataStore can have table names only with letters and numbers only

0
votes

I had the same issue, the function was passing the containerNameKey as string. below is the code that gave error

container = blobClient.GetContainerReference(containerNameKey) 

I changed it to

container = blobClient.GetContainerReference(ConfigurationManager.AppSettings(containerNameKey).ToString()) 

It worked

0
votes

I got 400-BadRequest response when I tried to put too long value to the string field.

"A UTF-16-encoded value. String values may be up to 64 KiB in size. Note that the maximum number of characters supported is about 32 K or less."

https://docs.microsoft.com/en-us/rest/api/storageservices/understanding-the-table-service-data-model

0
votes

For me I used table name with dash example: tablename: "table-1234", but tablename: "table1234" without dash worked.

0
votes

For me, I tried to insert an entity into an Azure Storage Table with a PartitionKey/RowKey that included the "#" character. Annoying that this is disallowed, since it is common to use "#" as a separator in single-table design.