API based plugin get null input but the variable is not null #4333

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

Originally created by @forrestsocool on GitHub (Jun 29, 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).
  • 请务必使用英文提交 Issue,否则会被关闭。谢谢!:)
  • Please do not modify this template :) and fill in all the required fields.

Dify version

0.6.12

Cloud or Self Hosted

Cloud

Steps to reproduce

api based plugin in the workflow, the input "article_ids" is not null, but server recieve null.
image

✔️ Expected Behavior

plugin should post it's input, regardless of it's special character or something, it's user's stuff.

Actual Behavior

server recieve null

Originally created by @forrestsocool on GitHub (Jun 29, 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] 请务必使用英文提交 Issue,否则会被关闭。谢谢!:) - [X] Please do not modify this template :) and fill in all the required fields. ### Dify version 0.6.12 ### Cloud or Self Hosted Cloud ### Steps to reproduce api based plugin in the workflow, the input "article_ids" is not null, but server recieve null. ![image](https://github.com/langgenius/dify/assets/3960433/033db388-90b5-4997-a72b-eea6b3bc0717) ### ✔️ Expected Behavior plugin should post it's input, regardless of it's special character or something, it's user's stuff. ### ❌ Actual Behavior server recieve null
yindo added the 🐞 bug label 2026-02-21 18:05:50 -05:00
yindo closed this issue 2026-02-21 18:05:50 -05:00
Author
Owner

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

The server is receiving the "article_ids" input as null because the inputs dictionary in the _convert_to_http_request_node method is constructed using variables from the variables list, but it does not include "article_ids". The inputs dictionary is populated with variables using the following code:

inputs = {}
for v in variables:
    inputs[v.variable] = '{{#start.' + v.variable + '#}}'

If "article_ids" is not part of the variables list, it will not be included in the inputs dictionary, resulting in it being null when the request is made [1].

Additionally, specific conditions or configurations in the workflow could cause "article_ids" to be excluded from the variables list. For example, in the BlockEnum.VariableAssigner case, the variables are determined by the variables property of the VariableAssignerNodeType data. If "article_ids" is not included in this property, it will be excluded from the variables list [2][3].

To debug this issue, you can use the "View Log—Details" and "View Log—Trace" features to get a comprehensive overview of the run, including input/output and metadata information. Additionally, the WorkflowLoggingCallback and LangSmithDataTrace classes provide detailed logging and tracing capabilities that can help identify why "article_ids" is being set to null [4][5][6][7][8].

To continue talking to Dosu, mention @dosu.

@dosubot[bot] commented on GitHub (Jun 29, 2024): <!-- Answer --> The server is receiving the "article_ids" input as null because the `inputs` dictionary in the `_convert_to_http_request_node` method is constructed using variables from the `variables` list, but it does not include "article_ids". The `inputs` dictionary is populated with variables using the following code: ```python inputs = {} for v in variables: inputs[v.variable] = '{{#start.' + v.variable + '#}}' ``` If "article_ids" is not part of the `variables` list, it will not be included in the `inputs` dictionary, resulting in it being null when the request is made <sup>[[1]](https://github.com/langgenius/dify/blob/main/api/services/workflow/workflow_converter.py)</sup>. Additionally, specific conditions or configurations in the workflow could cause "article_ids" to be excluded from the `variables` list. For example, in the `BlockEnum.VariableAssigner` case, the variables are determined by the `variables` property of the `VariableAssignerNodeType` data. If "article_ids" is not included in this property, it will be excluded from the `variables` list <sup>[[2]](https://github.com/langgenius/dify/blob/main/web/app/components/workflow/nodes/_base/components/variable/utils.ts)</sup><sup>[[3]](https://github.com/langgenius/dify/blob/main/web/app/components/workflow/constants.ts)</sup>. To debug this issue, you can use the "View Log—Details" and "View Log—Trace" features to get a comprehensive overview of the run, including input/output and metadata information. Additionally, the `WorkflowLoggingCallback` and `LangSmithDataTrace` classes provide detailed logging and tracing capabilities that can help identify why "article_ids" is being set to null <sup>[[4]](https://github.com/langgenius/dify-docs/blob/main/en/guides/workflow/debug-and-preview/log.md)</sup><sup>[[5]](https://github.com/langgenius/dify-docs/blob/main/zh_CN/guides/workflow/debug_and_preview/log.md)</sup><sup>[[6]](https://github.com/langgenius/dify-docs/blob/main/en/guides/workflow/debug-and-preview/step-run.md)</sup><sup>[[7]](https://github.com/langgenius/dify/blob/main/api/core/app/apps/workflow_logging_callback.py)</sup><sup>[[8]](https://github.com/langgenius/dify/blob/main/api/core/ops/langsmith_trace/langsmith_trace.py)</sup>. <!-- Dosu Comment Footer --> *To continue talking to [Dosu](https://dosu.dev), mention @dosu.*
Author
Owner

@forrestsocool commented on GitHub (Jun 29, 2024):

@dosu "article_ids" is included in this property as described in the picture, even you input the variable directly, it's still get null. [\"39277\", \"39287#39130\", \"38976#39087#38801\", \"39016#39060#38995#38768\", \"38923#39110#38880\", \"39289\", \"38924\", \"38967\", \"39017#39017\"]

@forrestsocool commented on GitHub (Jun 29, 2024): @dosu "article_ids" is included in this property as described in the picture, even you input the variable directly, it's still get null. `[\"39277\", \"39287#39130\", \"38976#39087#38801\", \"39016#39060#38995#38768\", \"38923#39110#38880\", \"39289\", \"38924\", \"38967\", \"39017#39017\"]`
Author
Owner

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

Ensure that the article_ids field is correctly populated in the inputs dictionary within the _convert_to_http_request_node method. Here is a detailed approach to verify and fix this:

  1. Check variables List:
    Ensure article_ids is included in the variables list:

    inputs = {}
    for v in variables:
        inputs[v.variable] = '{{#start.' + v.variable + '#}}'
    
  2. Manually Add article_ids:
    If article_ids is not automatically included, manually add it:

    if 'article_ids' not in inputs:
        inputs['article_ids'] = '{{#start.article_ids#}}'
    
  3. Verify request_body:
    Ensure article_ids is correctly referenced in the request_body:

    request_body = {
        'point': APIBasedExtensionPoint.APP_EXTERNAL_DATA_TOOL_QUERY.value,
        'params': {
            'app_id': app_model.id,
            'tool_variable': tool_variable,
            'inputs': inputs,
            'query': '{{#sys.query#}}' if app_model.mode == AppMode.CHAT.value else ''
        }
    }
    
  4. Check HttpExecutor Class:
    Ensure article_ids is correctly handled in the HttpExecutor class, particularly in the _init_template method:

    def _init_template(self, node_data: HttpRequestNodeData, variable_pool: Optional[VariablePool] = None):
        # extract all template in url
        self.server_url, server_url_variable_selectors = self._format_template(node_data.url, variable_pool)
        # extract all template in params
        params, params_variable_selectors = self._format_template(node_data.params, variable_pool)
        self.params = self._to_dict(params)
    
        # extract all template in headers
        headers, headers_variable_selectors = self._format_template(node_data.headers, variable_pool)
        self.headers = self._to_dict(headers)
    
        # extract all template in body
        body_data_variable_selectors = []
        if node_data.body:
            is_valid_json = self._is_json_body(node_data.body)
            body_data = node_data.body.data or ''
            if body_data:
                body_data, body_data_variable_selectors = self._format_template(body_data, variable_pool, is_valid_json)
    
            content_type_is_set = any(key.lower() == 'content-type' for key in self.headers)
            if node_data.body.type == 'json' and not content_type_is_set:
                self.headers['Content-Type'] = 'application/json'
            elif node_data.body.type == 'x-www-form-urlencoded' and not content_type_is_set:
                self.headers['Content-Type'] = 'application/x-www-form-urlencoded'
    
            if node_data.body.type in ['form-data', 'x-www-form-urlencoded']:
                body = self._to_dict(body_data)
                if node_data.body.type == 'form-data':
                    self.files = {k: ('', v) for k, v in body.items()}
                    random_str = lambda n: ''.join([chr(randint(97, 122)) for _ in range(n)])
                    self.boundary = f'----WebKitFormBoundary{random_str(16)}'
                    self.headers['Content-Type'] = f'multipart/form-data; boundary={self.boundary}'
                else:
                    self.body = urlencode(body)
            elif node_data.body.type in ['json', 'raw-text']:
                self.body = body_data
            elif node_data.body.type == 'none':
                self.body = ''
        self.variable_selectors = (server_url_variable_selectors + params_variable_selectors
                                   + headers_variable_selectors + body_data_variable_selectors)
    

By ensuring article_ids is correctly included and referenced in these steps, you should resolve the issue of it being received as null by the server [1][2].

To continue talking to Dosu, mention @dosu.

@dosubot[bot] commented on GitHub (Jun 29, 2024): <!-- Answer --> Ensure that the `article_ids` field is correctly populated in the `inputs` dictionary within the `_convert_to_http_request_node` method. Here is a detailed approach to verify and fix this: 1. **Check `variables` List**: Ensure `article_ids` is included in the `variables` list: ```python inputs = {} for v in variables: inputs[v.variable] = '{{#start.' + v.variable + '#}}' ``` 2. **Manually Add `article_ids`**: If `article_ids` is not automatically included, manually add it: ```python if 'article_ids' not in inputs: inputs['article_ids'] = '{{#start.article_ids#}}' ``` 3. **Verify `request_body`**: Ensure `article_ids` is correctly referenced in the `request_body`: ```python request_body = { 'point': APIBasedExtensionPoint.APP_EXTERNAL_DATA_TOOL_QUERY.value, 'params': { 'app_id': app_model.id, 'tool_variable': tool_variable, 'inputs': inputs, 'query': '{{#sys.query#}}' if app_model.mode == AppMode.CHAT.value else '' } } ``` 4. **Check `HttpExecutor` Class**: Ensure `article_ids` is correctly handled in the `HttpExecutor` class, particularly in the `_init_template` method: ```python def _init_template(self, node_data: HttpRequestNodeData, variable_pool: Optional[VariablePool] = None): # extract all template in url self.server_url, server_url_variable_selectors = self._format_template(node_data.url, variable_pool) # extract all template in params params, params_variable_selectors = self._format_template(node_data.params, variable_pool) self.params = self._to_dict(params) # extract all template in headers headers, headers_variable_selectors = self._format_template(node_data.headers, variable_pool) self.headers = self._to_dict(headers) # extract all template in body body_data_variable_selectors = [] if node_data.body: is_valid_json = self._is_json_body(node_data.body) body_data = node_data.body.data or '' if body_data: body_data, body_data_variable_selectors = self._format_template(body_data, variable_pool, is_valid_json) content_type_is_set = any(key.lower() == 'content-type' for key in self.headers) if node_data.body.type == 'json' and not content_type_is_set: self.headers['Content-Type'] = 'application/json' elif node_data.body.type == 'x-www-form-urlencoded' and not content_type_is_set: self.headers['Content-Type'] = 'application/x-www-form-urlencoded' if node_data.body.type in ['form-data', 'x-www-form-urlencoded']: body = self._to_dict(body_data) if node_data.body.type == 'form-data': self.files = {k: ('', v) for k, v in body.items()} random_str = lambda n: ''.join([chr(randint(97, 122)) for _ in range(n)]) self.boundary = f'----WebKitFormBoundary{random_str(16)}' self.headers['Content-Type'] = f'multipart/form-data; boundary={self.boundary}' else: self.body = urlencode(body) elif node_data.body.type in ['json', 'raw-text']: self.body = body_data elif node_data.body.type == 'none': self.body = '' self.variable_selectors = (server_url_variable_selectors + params_variable_selectors + headers_variable_selectors + body_data_variable_selectors) ``` By ensuring `article_ids` is correctly included and referenced in these steps, you should resolve the issue of it being received as null by the server <sup>[[1]](https://github.com/langgenius/dify/blob/main/api/services/workflow/workflow_converter.py)</sup><sup>[[2]](https://github.com/langgenius/dify/blob/main/api/core/workflow/nodes/http_request/http_executor.py)</sup>. <!-- Dosu Comment Footer --> *To continue talking to [Dosu](https://dosu.dev), mention @dosu.*
Author
Owner

@forrestsocool commented on GitHub (Jun 29, 2024):

哪个大聪明负责的Node输入输出的代码,bug真是多,还有好多没提的:
不同Node之间传输的变量也会被撸掉好多字符、
输出数组莫名其妙限制长度100、
输出字符串限制80k,
到处都是拍脑袋。。

@forrestsocool commented on GitHub (Jun 29, 2024): 哪个大聪明负责的Node输入输出的代码,bug真是多,还有好多没提的: 不同Node之间传输的变量也会被撸掉好多字符、 输出数组莫名其妙限制长度100、 输出字符串限制80k, 到处都是拍脑袋。。
Author
Owner

@huu3301 commented on GitHub (Jul 2, 2024):

@forrestsocool
@dosu
The same problem occur when I use the workflow as tool, that is because the output type of the tool is string, so the next block can not get the value of the parameter.
I'm not sure if this is an issue.
2

@huu3301 commented on GitHub (Jul 2, 2024): @forrestsocool @dosu The same problem occur when I use the workflow as tool, that is because the output type of the tool is string, so the next block can not get the value of the parameter. I'm not sure if this is an issue. ![2](https://github.com/langgenius/dify/assets/44219645/2429e8a8-44e4-4004-b71c-ec600891b507)
Author
Owner

@Sakura4036 commented on GitHub (Jul 4, 2024):

yes, the output type of workflow tools is string. If you want to get the output of the tool, you need to manually extract the variable using the code execution node as shown below, where output is the name of the variable output by the end node of the workflow tool:
image

@Sakura4036 commented on GitHub (Jul 4, 2024): yes, the output type of workflow tools is string. If you want to get the output of the tool, you need to manually extract the variable using the code execution node as shown below, where `output` is the name of the variable output by the end node of the workflow tool: ![image](https://github.com/langgenius/dify/assets/42060616/bc9f4324-d7a9-43f8-8c81-e3ed67f849aa)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify#4333