Skip to content

feat: add typed error system for executor#222

Merged
ako merged 3 commits intomendixlabs:mainfrom
retran:feature/typed-errors
Apr 17, 2026
Merged

feat: add typed error system for executor#222
ako merged 3 commits intomendixlabs:mainfrom
retran:feature/typed-errors

Conversation

@retran
Copy link
Copy Markdown
Contributor

@retran retran commented Apr 17, 2026

Summary

  • Introduce mdl/errors/ package with 6 structured error types (NotConnectedError, NotFoundError, AlreadyExistsError, UnsupportedError, ValidationError, BackendError) plus ErrExit sentinel
  • Migrate ~1,084 fmt.Errorf calls across 79 executor files to typed errors, enabling programmatic error classification via errors.Is/errors.As
  • 19 fmt.Errorf calls intentionally preserved (non-standard %w wrapping patterns)

Motivation

The executor currently uses ad-hoc fmt.Errorf strings for all errors — no structure, no classification. This makes it impossible for consumers (LSP, TUI, API backends) to programmatically distinguish "not connected" from "entity not found" from "permission denied."

Typed errors enable:

  • Dispatch registry (future MR): handlers return classified errors that the registry can route
  • LSP integration: map error types to diagnostic severity and code actions
  • API backend support: translate executor errors to structured API responses
  • .mpr v3 resilience: BackendError wraps I/O failures with operation context, decoupling error semantics from storage format

Error Types

Type Use case Example
NotConnectedError No project open, or not in write mode not connected to a project
NotFoundError Entity, microflow, page, etc. lookup failure entity not found: MyModule.MyEntity
AlreadyExistsError Conflict on create entity already exists: MyModule.MyEntity
UnsupportedError Unrecognized operation or feature unsupported attribute type: Binary
ValidationError Invalid input or state invalid entity name
BackendError I/O or storage failure wrapping a cause failed to write entity: disk full

Testing

  • Full errors.Is/errors.As contract tests in mdl/errors/errors_test.go
  • make build ✅, make test ✅, make lint

Notes

  • First commit is go fmt on 12 files with pre-existing formatting issues (unrelated to typed errors)
  • executor.ErrExit is re-exported from the new package for backward compatibility
  • No behavior changes — all existing consumers (CLI, TUI, LSP, diaglog) use .Error() only and remain fully compatible

Copilot AI review requested due to automatic review settings April 17, 2026 11:06
@github-actions
Copy link
Copy Markdown

AI Code Review

Critical Issues

  • None found

Moderate Issues

  • None found

Minor Issues

  • None found

What Looks Good

  • Comprehensive error system: Introduces 6 well-designed structured error types plus a sentinel that cover all common executor failure modes
  • Backward compatibility: All existing Error() message formats are preserved exactly, ensuring zero behavior changes for consumers that only use string output
  • Proper error wrapping: Correct implementation of Unwrap() methods and errors.Is/errors.As support throughout
  • Thorough migration: ~1,084 fmt.Errorf calls converted across 79 executor files with only 19 intentionally preserved (non-standard wrapping cases)
  • Complete test coverage: New mdl/errors/ package includes comprehensive unit tests validating the error contracts
  • Consistent application: Conversions follow clear patterns (e.g., NotConnected/NotConnectedWrite, Backend for I/O operations, Validation for input issues)
  • Clear documentation: PR description excellently explains motivation, error types, and migration strategy
  • No behavior changes: As stated, all existing consumers remain fully compatible since they only use .Error()

Recommendation

Approve. This PR successfully implements a typed error system for the executor that enables programmatic error classification while maintaining complete backward compatibility. The change is well-scoped, thoroughly tested, and follows Go error handling best practices. The migration appears complete and correct based on spot-checking multiple conversions.


Automated review via OpenRouter (Nemotron Super 120B) — workflow source

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new structured error package for the MDL executor and migrates executor commands/helpers away from ad-hoc fmt.Errorf strings to typed errors that can be classified programmatically (primarily via errors.As, and optionally errors.Is if implemented consistently).

Changes:

  • Add mdl/errors package with structured error types and contract tests.
  • Migrate many executor error returns to mdlerrors typed constructors (NewNotConnected, NewNotFound, NewAlreadyExists, NewValidation, NewUnsupported, NewBackend) and re-export ErrExit.
  • Apply a number of gofmt/alignment-only changes across SDK/model/AST files.

Reviewed changes

Copilot reviewed 83 out of 96 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
sdk/pages/pages_datasources.go gofmt/alignment only.
sdk/mpr/writer_rest.go gofmt/alignment only.
sdk/mpr/writer_datatransformer.go gofmt/alignment only.
sdk/mpr/parser_customblob.go gofmt/alignment only.
sdk/microflows/microflows_actions.go gofmt/alignment only.
sdk/agenteditor/types.go gofmt/alignment only.
model/types.go gofmt/alignment only.
mdl/executor/widget_registry.go Replace fmt.Errorf with typed backend/validation errors for widget definition loading/validation.
mdl/executor/widget_property.go Use typed validation/not-found errors for widget property updates.
mdl/executor/widget_engine.go Replace widget build/resolve errors with typed backend/validation/not-found errors.
mdl/executor/validate.go Convert validation-time errors to typed not-connected/not-found/validation errors.
mdl/executor/theme_reader.go Wrap theme registry I/O failures as typed backend errors.
mdl/executor/output_guard.go Convert line-limit abort to typed validation error.
mdl/executor/oql_type_inference.go Convert entity lookup failure to typed not-found error.
mdl/executor/helpers.go Convert module/folder lookup and cache errors to typed validation/backend/not-found errors.
mdl/executor/fragment_test.go Update test expectation to new “already exists” wording.
mdl/executor/executor_query.go Convert not-connected/unsupported query errors to typed errors.
mdl/executor/executor_dispatch.go Convert unknown statement type to typed unsupported error.
mdl/executor/executor_connect.go Convert connect/reconnect failures to typed backend/not-connected errors.
mdl/executor/executor.go Wrap finalization reconcile failure as typed backend error.
mdl/executor/cmd_workflows_write.go Convert workflow create/drop errors to typed errors.
mdl/executor/cmd_workflows.go Convert workflow list/describe errors to typed backend/not-found errors.
mdl/executor/cmd_widgets.go Convert widget show/update errors to typed backend/not-connected/unsupported errors.
mdl/executor/cmd_styling.go Convert styling show/describe/alter errors to typed errors.
mdl/executor/cmd_structure.go Convert structure/catalog/hierarchy errors to typed backend/not-connected errors.
mdl/executor/cmd_sql.go Convert SQL connection/parse failures to typed not-found/backend errors.
mdl/executor/cmd_snippets.go Convert snippet listing errors to typed backend errors.
mdl/executor/cmd_security.go Convert security show/describe errors to typed validation/backend/not-found/unsupported errors.
mdl/executor/cmd_search.go Convert search query argument and DB errors to typed validation/backend errors.
mdl/executor/cmd_rest_clients.go Convert REST client show/create/drop errors to typed errors.
mdl/executor/cmd_rename.go Convert rename errors to typed not-connected/backend/not-found/unsupported errors.
mdl/executor/cmd_published_rest.go Convert published REST show/create/alter/drop errors to typed errors.
mdl/executor/cmd_pages_show.go Convert page listing errors to typed backend errors.
mdl/executor/cmd_pages_describe.go Convert page/snippet/layout describe errors to typed backend/not-found errors.
mdl/executor/cmd_pages_create_v3.go Convert V3 page/snippet create/replace errors to typed errors.
mdl/executor/cmd_pages_builder_v3_widgets.go Convert widget-builder failures to typed backend/validation/not-found errors.
mdl/executor/cmd_pages_builder_v3_pluggable.go Convert attribute path validation error to typed validation error.
mdl/executor/cmd_pages_builder_input.go Convert ref resolution errors to typed validation/backend/not-found errors.
mdl/executor/cmd_pages_builder.go Convert page builder init/resolve/drop errors to typed backend/not-found/already-exists errors.
mdl/executor/cmd_page_wireframe.go Convert wireframe generation errors to typed errors.
mdl/executor/cmd_oql_plan.go Convert JSON marshal failure to typed backend error.
mdl/executor/cmd_navigation.go Convert navigation update errors to typed errors.
mdl/executor/cmd_modules.go Convert module create/drop/show/describe errors to typed errors.
mdl/executor/cmd_module_overview.go Convert module overview errors to typed backend/not-connected errors.
mdl/executor/cmd_misc.go Re-export ErrExit from mdl/errors and convert misc command errors to typed errors in several places.
mdl/executor/cmd_microflows_show.go Convert microflow/nanoflow show/describe errors to typed backend/not-found errors.
mdl/executor/cmd_microflows_drop.go Convert microflow drop errors to typed not-connected-write/backend/not-found errors.
mdl/executor/cmd_microflows_create.go Convert microflow create-time errors to typed errors (entity/enumeration resolution, create/update).
mdl/executor/cmd_microflows_builder.go gofmt/alignment only.
mdl/executor/cmd_microflow_elk.go Convert ELK generation errors to typed errors.
mdl/executor/cmd_mermaid.go Convert Mermaid describe errors to typed not-connected/backend/unsupported/not-found errors.
mdl/executor/cmd_lint.go Convert lint preconditions and failures to typed errors.
mdl/executor/cmd_layouts.go Convert layout listing errors to typed backend errors.
mdl/executor/cmd_languages.go Convert catalog precondition/query failures to typed validation/backend errors.
mdl/executor/cmd_jsonstructures.go Convert JSON structure CRUD/list errors to typed errors.
mdl/executor/cmd_javascript_actions.go Convert JS action show/describe errors to typed backend/not-found errors.
mdl/executor/cmd_javaactions.go Convert Java action CRUD/show/describe errors to typed errors.
mdl/executor/cmd_import_mappings.go Convert import mapping show/describe/create/drop errors to typed errors.
mdl/executor/cmd_import.go Convert import execution/link resolution errors to typed validation/backend/not-found/unsupported errors.
mdl/executor/cmd_imagecollections.go Convert image collection CRUD/show/describe errors to typed errors.
mdl/executor/cmd_fragments.go Convert fragment define/describe errors to typed already-exists/not-found errors.
mdl/executor/cmd_folders.go Convert folder lookup/drop/move errors to typed errors.
mdl/executor/cmd_features.go Convert feature checking/showing errors to typed backend/unsupported errors.
mdl/executor/cmd_export_mappings.go Convert export mapping show/describe/create/drop errors to typed errors.
mdl/executor/cmd_enumerations.go Convert enumeration CRUD/list/describe errors to typed errors.
mdl/executor/cmd_entities_describe.go Convert entity show/describe errors to typed validation/backend/not-found errors.
mdl/executor/cmd_domainmodel_elk.go Convert domain model ELK errors to typed errors.
mdl/executor/cmd_diff_local.go Convert diff-local preconditions and git/file failures to typed errors.
mdl/executor/cmd_diff.go Convert diff precondition error to typed not-connected error.
mdl/executor/cmd_dbconnection.go Convert DB connection CRUD/show/describe errors to typed errors.
mdl/executor/cmd_datatransformer.go Convert data transformer list/describe/create/drop errors to typed errors.
mdl/executor/cmd_contract.go Convert contract parsing/describe/create external entities errors to typed errors.
mdl/executor/cmd_context.go Convert show-context input/not-found errors to typed validation/not-found errors.
mdl/executor/cmd_constants.go Convert constant CRUD/show/describe errors to typed errors.
mdl/executor/cmd_catalog.go Convert catalog build/query/describe/search errors to typed errors.
mdl/executor/cmd_businessevents.go Convert business events CRUD/show/describe errors to typed errors.
mdl/executor/cmd_associations.go Convert association CRUD/show/describe errors to typed errors.
mdl/executor/cmd_agenteditor_models.go Convert agent editor model show/describe errors to typed errors.
mdl/executor/cmd_agenteditor_mcpservices.go Convert agent editor MCP service show/describe errors to typed errors.
mdl/executor/cmd_agenteditor_kbs.go Convert agent editor KB show/describe errors to typed errors.
mdl/executor/cmd_agenteditor_agents.go Convert agent editor agent show/describe errors to typed errors.
mdl/errors/errors_test.go Add contract tests for new typed error behaviors.
mdl/errors/errors.go Introduce new typed error definitions and constructors.
mdl/ast/ast_rest.go gofmt/alignment only.
mdl/ast/ast_query.go gofmt/alignment only.
mdl/ast/ast_microflow.go gofmt/alignment only.
mdl/ast/ast_entity.go gofmt/alignment only.
cmd/mxcli/docker/detect.go Comment formatting only.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread mdl/executor/cmd_microflows_create.go
Comment thread mdl/executor/cmd_navigation.go
Comment thread mdl/executor/cmd_widgets.go Outdated
Comment thread mdl/executor/cmd_microflows_create.go
Comment thread mdl/errors/errors.go Outdated
Comment thread mdl/executor/cmd_features.go
Comment thread mdl/executor/cmd_features.go Outdated
Comment thread mdl/executor/cmd_microflows_create.go
Comment thread mdl/executor/cmd_microflows_create.go
Introduce mdl/errors/ package with 6 structured error types:
NotConnectedError, NotFoundError, AlreadyExistsError,
UnsupportedError, ValidationError, BackendError.

Migrate all ~1,084 fmt.Errorf calls across 79 executor files
to typed errors. Enables programmatic error classification via
errors.Is/errors.As for future dispatch registry, LSP integration,
and API backend support.

All existing consumers (CLI, TUI, LSP, diaglog) use .Error() only
and remain fully compatible. ErrExit sentinel re-exported from
executor package for backward compatibility.
@retran retran force-pushed the feature/typed-errors branch from 626af7d to caed015 Compare April 17, 2026 11:12
@github-actions
Copy link
Copy Markdown

AI Code Review

What Looks Good:

  • The PR introduces a structured error system for the MDL executor, replacing ad-hoc fmt.Errorf calls with typed errors (NotConnectedError, NotFoundError, AlreadyExistsError, UnsupportedError, ValidationError, BackendError) and re-exporting ErrExit. It enables programmatic error handling via errors.Is/errors.Acm and includes test coverage. The PR contains no MDL syntax changes; all modifications are confined to Go code (packages mdl/errors/ and executor files), involving only error type definitions, their constructors, and replacements of fmt.Errorf calls with these typed errors. No grammar, AST, visitor, executor logic for MDL statements, LSP, DESCRIBE, or VSCode extension updates are touched. Since the PR does not add or modify MDL syntax, the checklist items pertaining to MDL syntax design and full-stack consistency are not applicable. Test coverage for the new error types is present (mdl/errors/errors_test.go). No overlapping or duplicated functionality is introduced; the PR refactors error handling consistently across executors. Therefore, there are no violations of the checklist.

Automated review via OpenRouter (Nemotron Super 120B) — workflow source

- Fix errors.go doc: clarify errors.As for classification, errors.Is for sentinels
- Preserve context in NotFound messages for parameter/return type validation
- Include full message in navigation profile NotFound error
- Use ValidationError for version parse failures instead of BackendError
- Include 'unsupported' prefix in container type UnsupportedError message
@github-actions
Copy link
Copy Markdown

AI Code Review

What Looks Good

  • Comprehensive error system: Introduces 6 structured error types plus ErrExit sentinel, covering all relevant error scenarios in the executor
  • Backward compatibility: All error types preserve original messages via Error() method, ensuring existing consumers (CLI, TUI, LSP, diaglog) remain unaffected
  • Proper error contracts: Comprehensive test suite verifies errors.Is/errors.As contracts work correctly, including wrapping scenarios
  • Consistent application: Migrated ~1,084 fmt.Errorf calls across 79 executor files to use appropriate typed errors
  • Thoughtful preservation: 19 fmt.Errorf calls intentionally preserved due to non-standard %w wrapping patterns shows careful consideration
  • Forward-looking design: Enables future features like dispatch registry, LSP integration, API backend support, and .mpr v3 resilience
  • Test coverage: New mdl/errors package has comprehensive test file (159 lines) covering all error types and contracts
  • Build verification: PR notes make build ✅, make test ✅, make lint
  • Separation of concerns: First commit is just go fmt on 12 unrelated files (separated concerns)

Recommendation

Approve. This is a well-executed, scoped refactor that significantly improves error handling for programmatic consumption while maintaining full backward compatibility. The typed error system will enable better error handling for consumers (LSP, TUI, API backends) without breaking existing consumers that only use .Error(). The refactor is well-tested, consistently applied, and forward-looking. The PR follows all relevant checklist items for non-syntax changes. Only list sections that have findings — skip empty sections.


Automated review via OpenRouter (Nemotron Super 120B) — workflow source

@ako
Copy link
Copy Markdown
Collaborator

ako commented Apr 17, 2026

Review

Overall: well-structured error system with a mechanical but correct migration across 79 files. CI is green, error messages are preserved, and the test coverage on the error types themselves is thorough. This is solid infrastructure that #224 builds on.

What I like

  • Clean error taxonomy. Six types that cover the executor's actual failure modes without over-categorizing. NotConnectedError with WriteMode flag, NotFoundError with Kind/Name fields, BackendError with Op+Unwrap — the right metadata for each class.
  • Only BackendError supports Unwrap. The other five are leaf errors. This is a deliberate design choice that prevents confusing errors.Is chains and keeps the error tree flat. Good.
  • ErrExit re-exported for backward compat (executor.ErrExit = mdlerrors.ErrExit). Callers that do errors.Is(err, executor.ErrExit) keep working. No breaking change.
  • Error message text preserved. Spot-checked multiple migrations — the .Error() output is identical before and after. No consumer sees a change.
  • Classifications are correct. NewNotFound is consistently used for element lookups, NewBackend for I/O wrapping, NewValidation for input constraints. No misclassifications found in spot checks.
  • Test quality. errors.As/errors.Is contract tests, including double-wrapping and nil-cause edge cases on BackendError. Exactly the right tests for an error package.

Concerns

  1. go fmt noise (same issue as feat: Support local file metadata for OData clients #210 and feat: dispatch registry, backend interfaces, and MockBackend #224). ast_entity.go, ast_microflow.go, ast_query.go, ast_rest.go, docker/detect.go are whitespace realignment unrelated to typed errors. The PR description acknowledges this ("First commit is go fmt on 12 files"), which is better than bundling silently, but it should still be a separate PR or pre-landed commit. Three PRs in a row with the same go fmt changes will conflict with each other.

  2. 925 → 1085 asymmetry. 925 fmt.Errorf removed, 1085 mdlerrors.Xxx added. The delta suggests some error sites were split (one fmt.Errorf became multiple typed errors) or new error paths were added. Worth a brief note in the PR description on what accounts for the ~160-line difference — makes the migration auditable.

  3. 19 intentionally-preserved fmt.Errorf calls. PR description mentions these are "non-standard %w wrapping patterns." Consider adding a // nolint:mdlerrors or // TODO: migrate comment on each so they're discoverable later and don't look like oversights.

  4. ValidationError is the catch-all. It has no structured fields — just a message string. Every other error type has metadata (Kind/Name, WriteMode, Op/Err). If a consumer needs to distinguish "invalid entity name" from "invalid XPath expression" from "invalid constant value," they'd need to string-match. Consider adding at least a Kind field for future use, even if it's empty for now.

  5. No Is() on typed errors. NotFoundError{Kind: "entity", Name: "Foo"} doesn't equal another NotFoundError{Kind: "entity", Name: "Foo"} via errors.Is. Only errors.As works. This is fine for the current use case (classify, not compare) but worth documenting as a design decision since users familiar with sentinel patterns might expect Is() to work on instances.

Minor

  • NewNotFoundMsg and NewAlreadyExistsMsg constructors let callers override the message entirely — means .Kind/.Name might not match .Error(). Acceptable for flexibility but could lead to inconsistency. Trust callers.
  • Import alias mdlerrors is consistent across all 79 files. Good discipline.

Verdict

Approve. This should land before #224 (which depends on it). Concerns (1) and (2) are cleanup; (3)–(5) are suggestions for future iterations. No blockers.

@ako ako merged commit 9be2604 into mendixlabs:main Apr 17, 2026
2 checks passed
@retran retran deleted the feature/typed-errors branch April 17, 2026 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants