The OpenAPI description of the custom tool does not seem to support JSON arrays. #4084

Closed
opened 2026-02-21 18:04:41 -05:00 by yindo · 6 comments
Owner

Originally created by @naonaolerobi on GitHub (Jun 13, 2024).

Self Checks

  • This is only for bug report, if you would like to ask a question, please head to Discussions.
  • I have searched for existing issues search for existing issues, including closed ones.
  • I confirm that I am using English to submit this report (我已阅读并同意 Language Policy).
  • Please do not modify this template :) and fill in all the required fields.

Dify version

0.6.10

Cloud or Self Hosted

Self Hosted (Docker)

Steps to reproduce

Hello, I was trying to define a custom tool which has a array jsonbody. But the tool simlator cannot recongnized as a array jsonbody request. Has anyone encountered a similar scenario? If you have a solution, it would be greatly appreciated.
The openapi description as delow:
openapi: 3.1.0
info:
title: test LLM to fulfill the json
description: test LLM to fulfill the json
version: v1.0.0
servers:

  • url: http://x.x.x.x:80
    paths:
    /println:
    post:
    description: test LLM to fulfill the json
    operationId: test LLM to fulfill the json
    requestBody:
    required: true
    content:
    application/json:
    schema:
    type: array
    items:
    type: object
    properties:
    component:
    type: string
    example: "MYSQL"
    cpu:
    type: string
    example: "4"
    ip:
    type: string
    example: "127.0.0.1"
    memory:
    type: string
    example: "16"
    storage:
    type: string
    example: "20"
    But the tool looks like not work.
    image

✔️ Expected Behavior

No response

Actual Behavior

No response

Originally created by @naonaolerobi on GitHub (Jun 13, 2024). ### Self Checks - [X] This is only for bug report, if you would like to ask a question, please head to [Discussions](https://github.com/langgenius/dify/discussions/categories/general). - [X] I have searched for existing issues [search for existing issues](https://github.com/langgenius/dify/issues), including closed ones. - [X] I confirm that I am using English to submit this report (我已阅读并同意 [Language Policy](https://github.com/langgenius/dify/issues/1542)). - [X] Please do not modify this template :) and fill in all the required fields. ### Dify version 0.6.10 ### Cloud or Self Hosted Self Hosted (Docker) ### Steps to reproduce Hello, I was trying to define a custom tool which has a array jsonbody. But the tool simlator cannot recongnized as a array jsonbody request. Has anyone encountered a similar scenario? If you have a solution, it would be greatly appreciated. The openapi description as delow: openapi: 3.1.0 info: title: test LLM to fulfill the json description: test LLM to fulfill the json version: v1.0.0 servers: - url: http://x.x.x.x:80 paths: /println: post: description: test LLM to fulfill the json operationId: test LLM to fulfill the json requestBody: required: true content: application/json: schema: type: array items: type: object properties: component: type: string example: "MYSQL" cpu: type: string example: "4" ip: type: string example: "127.0.0.1" memory: type: string example: "16" storage: type: string example: "20" But the tool looks like not work. ![image](https://github.com/langgenius/dify/assets/12195757/ae80ef57-3ddd-4ecb-9ce6-fe3ac6f4591d) ### ✔️ Expected Behavior _No response_ ### ❌ Actual Behavior _No response_
yindo added the 🐞 bug label 2026-02-21 18:04:41 -05:00
yindo closed this issue 2026-02-21 18:04:41 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Jun 13, 2024):

@naonaolerobi Hello! I'm here to help you with any issues or questions you may have about the Dify tool. If you need assistance, feel free to ask.

The issue with the tool simulator not recognizing the JSON array request might be due to how the request body is being handled in the do_http_request method. Specifically, the code needs to correctly handle the JSON array in the request body.

Here is a potential solution to ensure that the JSON array is correctly recognized and sent in the request body:

  1. Ensure that the body variable is correctly populated with the JSON array.
  2. Ensure that the Content-Type header is set to application/json.

Here is the relevant part of the do_http_request method with the necessary adjustments:

def do_http_request(self, url: str, method: str, headers: dict[str, Any], parameters: dict[str, Any]) -> httpx.Response:
    """
        do http request depending on api bundle
    """
    method = method.lower()

    params = {}
    path_params = {}
    body = []
    cookies = {}

    # check parameters
    for parameter in self.api_bundle.openapi.get('parameters', []):
        if parameter['in'] == 'path':
            value = ''
            if parameter['name'] in parameters:
                value = parameters[parameter['name']]
            elif parameter['required']:
                raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}")
            else:
                value = (parameter.get('schema', {}) or {}).get('default', '')
            path_params[parameter['name']] = value

        elif parameter['in'] == 'query':
            value = ''
            if parameter['name'] in parameters:
                value = parameters[parameter['name']]
            elif parameter.get('required', False):
                raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}")
            else:
                value = (parameter.get('schema', {}) or {}).get('default', '')
            params[parameter['name']] = value

        elif parameter['in'] == 'cookie':
            value = ''
            if parameter['name'] in parameters:
                value = parameters[parameter['name']]
            elif parameter.get('required', False):
                raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}")
            else:
                value = (parameter.get('schema', {}) or {}).get('default', '')
            cookies[parameter['name']] = value

        elif parameter['in'] == 'header':
            value = ''
            if parameter['name'] in parameters:
                value = parameters[parameter['name']]
            elif parameter.get('required', False):
                raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}")
            else:
                value = (parameter.get('schema', {}) or {}).get('default', '')
            headers[parameter['name']] = value

    # check if there is a request body and handle it
    if 'requestBody' in self.api_bundle.openapi and self.api_bundle.openapi['requestBody'] is not None:
        # handle json request body
        if 'content' in self.api_bundle.openapi['requestBody']:
            for content_type in self.api_bundle.openapi['requestBody']['content']:
                headers['Content-Type'] = content_type
                body_schema = self.api_bundle.openapi['requestBody']['content'][content_type]['schema']
                if body_schema['type'] == 'array':
                    body = parameters  # Assuming parameters is the JSON array
                else:
                    required = body_schema['required'] if 'required' in body_schema else []
                    properties = body_schema['properties'] if 'properties' in body_schema else {}
                    for name, property in properties.items():
                        if name in parameters:
                            # convert type
                            body[name] = self._convert_body_property_type(property, parameters[name])
                        elif name in required:
                            raise ToolParameterValidationError(
                                f"Missing required parameter {name} in operation {self.api_bundle.operation_id}"
                            )
                        elif 'default' in property:
                            body[name] = property['default']
                        else:
                            body[name] = None
                break

    # replace path parameters
    for name, value in path_params.items():
        url = url.replace(f'{{{name}}}', f'{value}')

    # parse http body data if needed, for GET/HEAD/OPTIONS/TRACE, the body is ignored
    if 'Content-Type' in headers:
        if headers['Content-Type'] == 'application/json':
            body = json_dumps(body)
        elif headers['Content-Type'] == 'application/x-www-form-urlencoded':
            body = urlencode(body)
        else:
            body = body

    # do http request
    if method == 'get':
        response = ssrf_proxy.get(url, params=params, headers=headers, cookies=cookies, timeout=API_TOOL_DEFAULT_TIMEOUT, follow_redirects=True)
    elif method == 'post':
        response = ssrf_proxy.post(url, params=params, headers=headers, cookies=cookies, data=body, timeout=API_TOOL_DEFAULT_TIMEOUT, follow_redirects=True)
    elif method == 'put':
        response = ssrf_proxy.put(url, params=params, headers=headers, cookies=cookies, data=body, timeout=API_TOOL_DEFAULT_TIMEOUT, follow_redirects=True)
    elif method == 'delete':
        response = ssrf_proxy.delete(url, params=params, headers=headers, cookies=cookies, data=body, timeout=API_TOOL_DEFAULT_TIMEOUT, allow_redirects=True)
    elif method == 'patch':
        response = ssrf_proxy.patch(url, params=params, headers=headers, cookies=cookies, data=body, timeout=API_TOOL_DEFAULT_TIMEOUT, follow_redirects=True)
    elif method == 'head':
        response = ssrf_proxy.head(url, params=params, headers=headers, cookies=cookies, timeout=API_TOOL_DEFAULT_TIMEOUT, follow_redirects=True)
    elif method == 'options':
        response = ssrf_proxy.options(url, params=params, headers=headers, cookies=cookies, timeout=API_TOOL_DEFAULT_TIMEOUT, follow_redirects=True)
    else:
        raise ValueError(f'Invalid http method {method}')

    return response

This adjustment ensures that the body variable is correctly populated with the JSON array and that the Content-Type header is set to application/json. This should help the tool simulator recognize the JSON array request correctly [1].

To continue talking to Dosu, mention @dosu.

@dosubot[bot] commented on GitHub (Jun 13, 2024): <!-- Greeting --> @naonaolerobi Hello! I'm here to help you with any issues or questions you may have about the Dify tool. If you need assistance, feel free to ask. <!-- Answer --> The issue with the tool simulator not recognizing the JSON array request might be due to how the request body is being handled in the `do_http_request` method. Specifically, the code needs to correctly handle the JSON array in the request body. Here is a potential solution to ensure that the JSON array is correctly recognized and sent in the request body: 1. Ensure that the `body` variable is correctly populated with the JSON array. 2. Ensure that the `Content-Type` header is set to `application/json`. Here is the relevant part of the `do_http_request` method with the necessary adjustments: ```python def do_http_request(self, url: str, method: str, headers: dict[str, Any], parameters: dict[str, Any]) -> httpx.Response: """ do http request depending on api bundle """ method = method.lower() params = {} path_params = {} body = [] cookies = {} # check parameters for parameter in self.api_bundle.openapi.get('parameters', []): if parameter['in'] == 'path': value = '' if parameter['name'] in parameters: value = parameters[parameter['name']] elif parameter['required']: raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}") else: value = (parameter.get('schema', {}) or {}).get('default', '') path_params[parameter['name']] = value elif parameter['in'] == 'query': value = '' if parameter['name'] in parameters: value = parameters[parameter['name']] elif parameter.get('required', False): raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}") else: value = (parameter.get('schema', {}) or {}).get('default', '') params[parameter['name']] = value elif parameter['in'] == 'cookie': value = '' if parameter['name'] in parameters: value = parameters[parameter['name']] elif parameter.get('required', False): raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}") else: value = (parameter.get('schema', {}) or {}).get('default', '') cookies[parameter['name']] = value elif parameter['in'] == 'header': value = '' if parameter['name'] in parameters: value = parameters[parameter['name']] elif parameter.get('required', False): raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}") else: value = (parameter.get('schema', {}) or {}).get('default', '') headers[parameter['name']] = value # check if there is a request body and handle it if 'requestBody' in self.api_bundle.openapi and self.api_bundle.openapi['requestBody'] is not None: # handle json request body if 'content' in self.api_bundle.openapi['requestBody']: for content_type in self.api_bundle.openapi['requestBody']['content']: headers['Content-Type'] = content_type body_schema = self.api_bundle.openapi['requestBody']['content'][content_type]['schema'] if body_schema['type'] == 'array': body = parameters # Assuming parameters is the JSON array else: required = body_schema['required'] if 'required' in body_schema else [] properties = body_schema['properties'] if 'properties' in body_schema else {} for name, property in properties.items(): if name in parameters: # convert type body[name] = self._convert_body_property_type(property, parameters[name]) elif name in required: raise ToolParameterValidationError( f"Missing required parameter {name} in operation {self.api_bundle.operation_id}" ) elif 'default' in property: body[name] = property['default'] else: body[name] = None break # replace path parameters for name, value in path_params.items(): url = url.replace(f'{{{name}}}', f'{value}') # parse http body data if needed, for GET/HEAD/OPTIONS/TRACE, the body is ignored if 'Content-Type' in headers: if headers['Content-Type'] == 'application/json': body = json_dumps(body) elif headers['Content-Type'] == 'application/x-www-form-urlencoded': body = urlencode(body) else: body = body # do http request if method == 'get': response = ssrf_proxy.get(url, params=params, headers=headers, cookies=cookies, timeout=API_TOOL_DEFAULT_TIMEOUT, follow_redirects=True) elif method == 'post': response = ssrf_proxy.post(url, params=params, headers=headers, cookies=cookies, data=body, timeout=API_TOOL_DEFAULT_TIMEOUT, follow_redirects=True) elif method == 'put': response = ssrf_proxy.put(url, params=params, headers=headers, cookies=cookies, data=body, timeout=API_TOOL_DEFAULT_TIMEOUT, follow_redirects=True) elif method == 'delete': response = ssrf_proxy.delete(url, params=params, headers=headers, cookies=cookies, data=body, timeout=API_TOOL_DEFAULT_TIMEOUT, allow_redirects=True) elif method == 'patch': response = ssrf_proxy.patch(url, params=params, headers=headers, cookies=cookies, data=body, timeout=API_TOOL_DEFAULT_TIMEOUT, follow_redirects=True) elif method == 'head': response = ssrf_proxy.head(url, params=params, headers=headers, cookies=cookies, timeout=API_TOOL_DEFAULT_TIMEOUT, follow_redirects=True) elif method == 'options': response = ssrf_proxy.options(url, params=params, headers=headers, cookies=cookies, timeout=API_TOOL_DEFAULT_TIMEOUT, follow_redirects=True) else: raise ValueError(f'Invalid http method {method}') return response ``` This adjustment ensures that the `body` variable is correctly populated with the JSON array and that the `Content-Type` header is set to `application/json`. This should help the tool simulator recognize the JSON array request correctly <sup>[[1]](https://github.com/langgenius/dify/blob/main/api/core/tools/tool/api_tool.py)</sup>. <!-- Dosu Comment Footer --> *To continue talking to [Dosu](https://dosu.dev), mention @dosu.*
Author
Owner

@crazywoola commented on GitHub (Jun 14, 2024):

Do not support array[object] I think, you could change to array[number] or array[string] to see if it works.

@crazywoola commented on GitHub (Jun 14, 2024): Do not support array[object] I think, you could change to array[number] or array[string] to see if it works.
Author
Owner

@naonaolerobi commented on GitHub (Jun 19, 2024):

Do not support array[object] I think, you could change to array[number] or array[string] to see if it works.

And then still cannot support even use the array[string]. Any other ideas for this issue?:)

@naonaolerobi commented on GitHub (Jun 19, 2024): > Do not support array[object] I think, you could change to array[number] or array[string] to see if it works. And then still cannot support even use the array[string]. Any other ideas for this issue?:)
Author
Owner

@elvis-cai commented on GitHub (Jul 24, 2024):

+1 had the same issue

@elvis-cai commented on GitHub (Jul 24, 2024): +1 had the same issue
Author
Owner

@soichisumi commented on GitHub (Sep 19, 2024):

Even array[string], it doesn't work.
The param of this api is not recognized as input of custom tool.
image

image

Could you reconfirm? @crazywoola

@soichisumi commented on GitHub (Sep 19, 2024): Even array[string], it doesn't work. The param of this api is not recognized as input of custom tool. ![image](https://github.com/user-attachments/assets/cc52e385-7043-43ed-a9a8-a00b0993c840) ![image](https://github.com/user-attachments/assets/9977530a-8e0a-4738-bc2a-77d4ad0ea33c) Could you reconfirm? @crazywoola
Author
Owner

@hh2712 commented on GitHub (Apr 25, 2025):

Has this problem been sorted out in the latest version? I had same issue as posted where custom tool variable in array of string cannot be identified in the canvas tool node. Please kindly let us know if there is a way around it.

@hh2712 commented on GitHub (Apr 25, 2025): Has this problem been sorted out in the latest version? I had same issue as posted where custom tool variable in array of string cannot be identified in the canvas tool node. Please kindly let us know if there is a way around it.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify#4084