{"record":{"id":"722ba7d658beacad","repo":"apache/shardingsphere","slug":"approved-steps-must-contain-only-s","errorCode":null,"errorMessage":"approved_steps must contain only %s.","messagePattern":"approved_steps must contain only (.+?)\\.","errorType":"validation","errorClass":"MCPInvalidApprovedStepsException","httpStatus":null,"severity":"error","filePath":"mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowExecutionService.java","lineNumber":122,"sourceCode":"    \n    private String requireExecutionMode(final WorkflowContextSnapshot snapshot, final String executionMode) {\n        if (executionMode.isEmpty()) {\n            throw new MCPExecutionModeRequiredException(WorkflowToolDescriptors.APPLY_TOOL_NAME, EXECUTION_MODES, createPreviewSuggestedArguments(snapshot));\n        }\n        String result = executionMode.toLowerCase(Locale.ENGLISH);\n        if (!EXECUTION_MODES.contains(result)) {\n            throw new MCPInvalidExecutionModeException(WorkflowToolDescriptors.APPLY_TOOL_NAME, EXECUTION_MODES, createPreviewSuggestedArguments(snapshot));\n        }\n        return result;\n    }\n    \n    private void requireApprovedSteps(final WorkflowContextSnapshot snapshot, final List<String> approvedSteps) {\n        if (null == approvedSteps || approvedSteps.isEmpty()) {\n            return;\n        }\n        for (String each : approvedSteps) {\n            if (!ALLOWED_APPROVAL_STEPS.contains(each)) {\n                throw new MCPInvalidApprovedStepsException(ALLOWED_APPROVAL_STEPS, createPreviewSuggestedArguments(snapshot));\n            }\n        }\n    }\n    \n    private Map<String, Object> checkApplyPreconditions(final String sessionId, final WorkflowContextSnapshot snapshot, final String executionMode,\n                                                        final List<String> approvedSteps) {\n        if (!WorkflowLifecycleUtils.isOwnedBySession(sessionId, snapshot)) {\n            return createRejectedResponse(snapshot, executionMode, WorkflowIssueCode.SESSION_OWNERSHIP_MISMATCH, \"The workflow plan belongs to another MCP session.\",\n                    \"Continue the workflow from the same session that created the plan.\");\n        }\n        if (!isApplicableStatus(snapshot)) {\n            return createRejectedResponse(snapshot, executionMode, WorkflowIssueCode.WORKFLOW_STATUS_INVALID,\n                    String.format(\"Workflow status `%s` cannot enter apply in the current lifecycle.\", snapshot.getStatus()),\n                    \"Plan the workflow again or continue from a reviewable status.\");\n        }\n        if (WorkflowLifecycle.EXECUTION_MODE_REVIEW_THEN_EXECUTE.equals(executionMode) && !WorkflowLifecycle.STATUS_PREVIEWED.equalsIgnoreCase(snapshot.getStatus())) {\n            return createRejectedResponse(snapshot, executionMode, WorkflowIssueCode.WORKFLOW_STATUS_INVALID,\n                    \"Automatic workflow execution requires an execution_mode=preview call first.\",","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/apache/shardingsphere/blob/e952770a215630a3659c75d64369168cd3e26b82/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowExecutionService.java#L104-L140","documentation":"Thrown by WorkflowExecutionService.requireApprovedSteps when the approved_steps list on an apply request contains at least one value outside ALLOWED_APPROVAL_STEPS. Null, absent, or empty lists are allowed (no approval filtering); only an unrecognized step name triggers MCPInvalidApprovedStepsException, which carries the allowed set and preview-suggested arguments.","triggerScenarios":"Sending approved_steps such as [\"everything\"] or [\"APPLY\"] when only specific step identifiers (the values in ALLOWED_APPROVAL_STEPS) are accepted. Any single bad entry rejects the whole list.","commonSituations":"An agent inventing step names to force full auto-apply, mismatched step vocabulary after a workflow engine upgrade, or free-text step labels copied from the plan's human-readable output instead of the machine identifiers.","solutions":["Use only the step identifiers listed in the MCPInvalidApprovedStepsException payload, matching exactly (no case or wording improvisation).","If you do not need step-level approval, omit approved_steps or send an empty list rather than guessing names.","Echo the step identifiers verbatim from the workflow plan artifact the plan tool returned, not from prose summaries."],"exampleFix":"// before\narguments.put(\"approved_steps\", List.of(\"apply-everything\"));\n// after\narguments.put(\"approved_steps\", List.of(/* exact identifiers from the plan artifact */));","handlingStrategy":"validation","validationCode":"List<String> unknown = approvedSteps.stream().filter(s -> !ALLOWED_APPROVAL_STEPS.contains(s)).toList();\nif (!unknown.isEmpty()) { throw new IllegalArgumentException(\"unknown steps: \" + unknown); }","typeGuard":"const stepsOk = steps => steps == null || steps.every(s => ALLOWED_APPROVAL_STEPS.includes(s));","tryCatchPattern":"try {\n    applyTool.call(request);\n} catch (final MCPInvalidApprovedStepsException ex) {\n    // drop invented step names; use only identifiers from the plan artifact or omit approved_steps\n}","preventionTips":["Echo machine step identifiers from the plan tool output, never prose","Omit approved_steps entirely when step filtering is not needed","Re-derive the allowed set after workflow engine upgrades"],"tags":["mcp","workflow","validation","approval-steps"],"backgroundTag":null,"analyzedSha":"e952770a215630a3659c75d64369168cd3e26b82","analyzedAt":"2026-08-14T13:54:53.392Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}