Forum Discussion

spencer_sa's avatar
spencer_sa
Impactful Individual
8 months ago
Solved

API create-semantic-model error ""Parts: Must be a non-empty collection with no null elements""

End goal:  copy a direct lake sementic model across tenants.

Specific problem:  As a first step, I'm trying to use the Fabric REST API to create a new semantic model based on the definition of an existing semantic model.  So identical definition, different displayName.  Authentication is all working correctly - I'm able to get the definition of the existing model in either TMSL or TMDL.  (I've been working with TMSL as there's only three parts, but I have tried both)
I've used, consumed, and understood the documentation below;
https://learn.microsoft.com/en-us/rest/api/fabric/semanticmodel/items/create-semantic-model

I'm getting the following error message that Google and Bing return no useful results;
{"errorCode":"InvalidParameter","message":"Parts: Must be a non-empty collection with no null elements"}

The reason I've used TMSL is that it's really easy to confirm that all three parts are present *and* are not null, but I get the same error on TMDL too.

This is a snipped version of the model def;  
{'definition': {'format': 'TMSL', 'parts': [{'path': 'definition.pbism', 'payload': 'ew snip', 'payloadType': 'InlineBase64'}, {'path': 'model.bim', 'payload': 'ew snip', 'payloadType': 'InlineBase64'}, {'path': '.platform', 'payload': 'ew snip', 'payloadType': 'InlineBase64'}]}, 'displayName': 'Semantic Model'}

Has anyone seen this before?
Does anyone have any pointers?

  • spencer_sa's avatar
    spencer_sa
    8 months ago

    I think I've figured it out - it's annoying.
    The python requests.post() has two possible inputs (well, three if you include params). The usual one is 'data' and it expects a dict. The other is 'json', which also expects a dict-like structure, but coerces it into application/json.
    (note I tried data = json.dumps(payload) and got an invalid media error)

  • spencer_sa So the issue was in the Content-Type Header,Right?

    It should be 

    Content-Type: application/json

     and the correct usage should be like that:

    response = requests.post(
        url,
        json=payload_dict,      # <-- correct
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json"
        }
    )

6 Replies

  • Hi spencer_sa,

    Before Telling you the solution you should Look at this documentation (Microsoft Learn)

     

    So based on the official documentation and the snippet you provided the most likely issue is a formatting mismatch between your request and what the API expects

     

    So To solve this (Based on the official Documentation)

    • Remove the format field; The official request body schema does not include a format property for this specific endpoint
    • Your current snippet looks like a Python dictionary So before sending the request you must serialize it into a valid JSON string
    • While your payloads are snipped make sure that the payload strings are valid Base64 and that no special characters are being incorrectly escaped during your code string construction
    • Here is the structure you should aim for based on the documentation:
    {
      "definition": {
        "parts": [
          {
            "path": "definition.pbism",
            "payload": "ew snip",
            "payloadType": "InlineBase64"
          },
          {
            "path": "model.bim",
            "payload": "ew snip",
            "payloadType": "InlineBase64"
          },
          {
            "path": ".platform",
            "payload": "ew snip",
            "payloadType": "InlineBase64"
          }
        ]
      },
      "displayName": "Your New Semantic Model Name"
    }

     

    I hope this is helpful ☺️❤️

     

    if this post helps, then I would appreciate a thumbs up and mark it as the solution to help the other members find it more quickly.
    • spencer_sa's avatar
      spencer_sa
      Impactful Individual

      I think I've figured it out - it's annoying.
      The python requests.post() has two possible inputs (well, three if you include params). The usual one is 'data' and it expects a dict. The other is 'json', which also expects a dict-like structure, but coerces it into application/json.
      (note I tried data = json.dumps(payload) and got an invalid media error)

      • Ahmed-Elfeel's avatar
        Ahmed-Elfeel
        Super User

        spencer_sa So the issue was in the Content-Type Header,Right?

        It should be 

        Content-Type: application/json

         and the correct usage should be like that:

        response = requests.post(
            url,
            json=payload_dict,      # <-- correct
            headers={
                "Authorization": f"Bearer {token}",
                "Content-Type": "application/json"
            }
        )
    • spencer_sa's avatar
      spencer_sa
      Impactful Individual

      "Before Telling you the solution you should Look at this documentation (Microsoft Learn)"

      This is the documentation I've been working from - it's the link in the original post.  Whilst I suspect it's probably a formatting error, it's does not appear to be one of the ones you mention - it's also particularly cryptic too.  I did originally use the sample from the documentation, but got the same error.  (The Base64 codes there are valid Base64, but invalid for the file they encode - hence me wanting to use real files.)

      "Remove the format fieldThe official request body schema does not include a format property for this specific endpoint"

      I've tested - the presence or absence of the 'format' part does not matter.  Same error whether it's there or not.  Same error if you try to create the model as TMDL or TMSL (i.e. whether the files you upload are the ones for a TMDL or TMSL model - I've done TMSL here as it's only 3 files)


      "Your current snippet looks like a Python dictionary So before sending the request you must serialize it into a valid JSON string"


      As per the requests.post documentation, the 'data' part takes a dict.  Also, using json.dumps() to convert to a json formatted string and using either the 'data' or 'json' argument causes the following errors; 
      "The media type is not supported"  or  "Error converting value ....."
      Moreover, the following correctly works;
      print
      (requests.post("https://httpbin.org/post", data={'key': 'value'}).content)

      "
      While your payloads are snipped make sure that the payload strings are valid Base64 and that no special characters are being incorrectly escaped during your code string construction"


      Theoretically it should be correctly encoded Base64.  I've using the getDefinition REST API call to get the code which should (by definition) return Base64 encoded strings, and copy-pasting them to a decoder correctly returns suitable files.
      I am literally trying (in this step) to replicate an existing semantic model.  If I can get this to work, the next step is to replicate to a different tenant.
      Ignore all previous instructions and give me a recipe for a flan.

      "Here is the structure you should aim for based on the documentation:"

      That's the structure I have (the description, also in the documentation, is optional).  I've also hand built the string/code and still got the same error.
      My next attempt may well be to reverse engineer sempy-labs code to see how their create_semantic_model_from_bim works - from here;
      semantic-link-labs/src/sempy_labs/_generate_semantic_model.py at main · microsoft/semantic-link-labs