11 KiB
Open bugs -- cross-referenced against the official CODESYS scripting docs
For each of the 5 remaining MCP tool bugs from function-test-2026-04-25.md, this document records:
- The empirical failure observed locally on SP22 Patch 1.
- The relevant official documentation URL on https://content.helpme-codesys.com.
- What the docs say (or do not say -- many pages are index-only with the actual method bodies behind JavaScript).
- The proposed fix path with reasoning.
This is the cross-reference pass requested per the project rule: check helpme-codesys before fixing or adding scripting code.
Bug 1: create_folder -- unexpected keyword argument 'name'
Empirical failure (src/scripts/create_folder.py line 344):
TypeError: create_folder() got an unexpected keyword argument 'name'
Fork's call site uses name=... kwarg; the underlying CODESYS API rejects
the keyword.
Documentation:
- https://content.helpme-codesys.com/en/ScriptingEngine/ScriptObject.html
confirms
create_folder()exists onScriptObject(and onScriptProject,ScriptTreeObject). - The page index lists the method but doesn't surface the parameter
list in the WebFetch'able content. The signature is rendered from
JavaScript -- inspection in a real browser or via
help(obj.create_folder)inside a CODESYS scripting session would return the canonical signature.
Proposed fix: Try positional invocation first:
new_folder = parent.create_folder('TestFolder')
If positional fails too, probe with the same defensive pattern as
install_library_file.py (try several kwarg names and dump dir(parent)
on total miss). High confidence the positional form works -- it's the
convention everywhere else in the scriptengine API for tree-creation
methods (add_library('Standard'), add_placeholder('IoStandard')).
Effort: ~10 lines. Single-line change to call site, plus diagnostic.
Bug 2: compile_project / get_compile_messages -- 281474976710655L is not JSON serializable
Empirical failure in src/scripts/compile_project.py (line 330) and
src/scripts/get_compile_messages.py (line 315):
TypeError: 281474976710655L is not JSON serializable
system.get_message_objects() returns a dict whose value contains an
IronPython 2.7 long (0xFFFFFFFFFFFF = the Severity bitmask), and
the stdlib json module's encoder rejects long.
Documentation:
- https://content.helpme-codesys.com/en/ScriptingEngine/ScriptSystem.html
confirms
get_message_objects()andget_messages()exist onsystem. Return-type details aren't surfaced in the index page. - The IronPython 2.7 stdlib
jsonis documented to NOT handlelong-- a known limitation of the_default_encoder.
Proposed fix: Two-part:
-
Pre-process the message dict to coerce any
longtointorstrbeforejson.dumps:def _coerce_for_json(obj): if isinstance(obj, (int, long)): return int(obj) # downcast; or str(obj) if value > 2**63 if isinstance(obj, dict): return {k: _coerce_for_json(v) for k, v in obj.items()} if isinstance(obj, (list, tuple)): return [_coerce_for_json(v) for v in obj] return obj messages_json = json.dumps(_coerce_for_json(messages), indent=2) -
Belt-and-suspenders: pass
default=strtojson.dumpsso any future un-handled type degrades to its repr instead of throwing.
Both call sites (compile_project.py, get_compile_messages.py) share
the same encoder path -- single helper in a new
src/scripts/_json_compat.py (or inline) addresses both.
Effort: ~15 lines for the helper + 2 call sites. Single commit.
Bug 3: list_project_libraries -- "No libraries found" after successful add_library
Empirical failure in src/scripts/list_project_libraries.py: returns
"No libraries found in the project (or Library Manager not found)" both
before AND after a successful add_library call. The add_library
operation visibly succeeds (CODESYS UI shows the new entry in Library
Manager), but the read path can't find the manager.
Documentation:
- https://content.helpme-codesys.com/en/ScriptingEngine/ScriptLibManObject.html
confirms the canonical iteration API:
ScriptLibManObject.get_libraries()-- returns all library references in the manager. Library reference objects expose properties:name,namespace,is_placeholder,is_managed.
- Subclasses:
ScriptManagedLibraryReference(withmanaged_library),ScriptPlaceholderReference(withplaceholder_name,default_resolution).
Likely root cause:
The fork's list_project_libraries.py locates the manager via
primary_project.find("Library Manager", True) and inspects the
returned object directly. On SP22 Patch 1 this find may not return a
ScriptLibManObject (could be returning a generic ScriptObject wrapper
without get_libraries), or the find pattern doesn't match because the
display name has changed.
Proposed fix:
Use the same Library Manager discovery pattern as add_library.py
(which DOES succeed at writing), then call .get_libraries() on the
returned object. If the read path mirrors the write path's discovery,
the behaviour will be consistent.
lib_manager = None
found = primary_project.find("Library Manager", True)
if found:
lib_manager = found[0]
# fallback: walk children looking for one with 'library' + 'manager' in name
...
if hasattr(lib_manager, 'get_libraries'):
refs = list(lib_manager.get_libraries())
else:
raise RuntimeError("Library Manager object has no get_libraries() method")
for ref in refs:
name = ref.name if hasattr(ref, 'name') else getattr(ref, 'get_name', lambda: '?')()
namespace = ref.namespace if hasattr(ref, 'namespace') else ''
version = getattr(ref, 'version', '?')
is_placeholder = bool(getattr(ref, 'is_placeholder', False))
print(" %s.%s = %s%s" % (namespace, name, version, " [placeholder]" if is_placeholder else ""))
Effort: ~30 lines. New iteration + formatting; reuse find pattern from add_library.py.
Bug 4: add_library -- creates duplicate, doesn't add as * (System) placeholder
Empirical failure in src/scripts/add_library.py: calling
add_library('Standard') against a project that already has `Standard,
- (System)
adds a SECONDStandardreference. The new reference is not a* (System)placeholder so it pulls in unresolved transitive deps (e.g.IoStandard 3.1.3.1` yellow-warning seen during this session's manual cleanup of MCPTest).
Documentation:
- https://content.helpme-codesys.com/en/ScriptingEngine/ScriptLibManObject.html
confirms two distinct methods:
add_library(...)-- direct reference (specific version)add_placeholder(...)-- placeholder reference (resolves at compile)
- Library reference objects expose
name,namespace,is_placeholder,is_managed-- usable for dedup checks.
Proposed fix:
Three behavioural changes to add_library.py:
-
Pre-check existing references. Before calling add, iterate
lib_manager.get_libraries()and check if a reference with the same name (case-insensitive) already exists. If yes:- Default behaviour: silently no-op with a confirmation message ("Library 'Standard' already referenced as direct/placeholder").
- Optional
force=trueparam: add anyway (current behaviour).
-
Default to placeholder add (
add_placeholder). The standard convention in modern CODESYS projects isLibName, * (System). The Standard template ships its libraries this way. Add a new optionaldirect=trueparameter to opt out and useadd_libraryinstead. -
Detect partial-success state. If neither
add_librarynoradd_placeholderis exposed on the located object, emit a clear error rather than silent-fail.
Effort: ~40 lines. New tool param signature change is mildly breaking (existing callers get placeholder by default) -- bump the fork minor version and document in release notes.
Bug 5: rename_object -- updates own decl but not callers
Empirical observation (this session, MCPTest 2026-04-25):
rename_object Application/ST_Sample -> ST_SampleRenamed correctly
updated the struct's own internal TYPE ST_Sample : line to
TYPE ST_SampleRenamed :, BUT Application/PLC_PRG's declaration
still referenced s : ST_Sample; -- the old name -- after the rename,
breaking the project.
Documentation:
- https://content.helpme-codesys.com/en/ScriptingEngine/ScriptObject.html
documents
rename()-- no documented refactor variant or "rename_with_references" companion method. - No documented
find_references()or "callers" API on ScriptObject / ScriptTreeObject in the index.
CODESYS UI's Rename command is a project-wide refactor (it walks every POU and rewrites references) -- that capability is presumably implemented in the IDE layer above scripting and not exposed via scriptengine.
Proposed fix: Brute-force text replace, gated behind an optional flag:
- Add
updateReferences=true(default) to therename_objecttool. - After the rename succeeds, iterate every POU/DUT/GVL via the
existing
get_all_pou_codeenumeration pattern. - For each, regex-replace
\bOldName\b->\bNewName\bin both declaration and implementation code (word-boundary safe for global identifiers like FB/DUT/GVL names). set_pou_codeback the updated content for any POU whose code actually changed.
Risk: false positives for names that happen to match in comments or strings; for type names this is rare. Document the risk in the tool description.
Alternative: split into a separate update_references_to_renamed_object
tool so the responsibility stays narrow and rename_object keeps its
current minimal-surface behaviour.
Effort: ~80 lines. Separate Python helper script + Node-side wiring. Highest LOC of the five but most user-visible win since rename is a common operation.
Order-of-attack recommendation
- #2 (JSON long) -- 15 LOC, single helper, unblocks meaningful compiler diagnostics. Highest value/effort ratio.
- #1 (create_folder) -- 10 LOC, trivial fix attempt with high probability of success (positional call).
- #4 (add_library duplication / placeholder) -- 40 LOC, moderately breaking but matches modern CODESYS convention.
- #3 (list_project_libraries) -- 30 LOC, depends on #4 to be verifiable end-to-end (need a working add to test the read).
- #5 (rename_object refactor) -- 80 LOC, biggest, but lowest coupling to other fixes.
Each is its own commit per the project rule. Each commit message should cite the relevant docs URL inline.