apache/shardingsphere · error · MCPInvalidExecutionModeException

%s execution_mode must be one of %s.

Error message

%s execution_mode must be one of %s.

What it means

After confirming execution_mode is present, resolveExecutionMode accepts only the exact values 'execute' or 'preview'; anything else throws MCPInvalidExecutionModeException ('%s execution_mode must be one of %s.'). The strict whitelist prevents accidental mutations via fuzzy values like 'run', 'dry-run', or differently-cased strings. Suggested arguments for a preview retry are attached to the exception.

Source

Thrown at mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExecuteUpdateToolHandler.java:105

    private ClassificationResult checkUpdateStatement(final MCPFeatureRequestContext requestContext, final MCPToolArguments toolArguments, final String sql) {
        ClassificationResult classificationResult = SQLExecutionToolHandlerSupport.analyze(requestContext, toolArguments, sql);
        if (SQLExecutionToolHandlerSupport.isQueryStatement(classificationResult)) {
            throw new SQLToolMismatchException("database_gateway_execute_update does not accept read-only SQL. Use database_gateway_execute_query for read-only SQL.",
                    CoreToolNames.EXECUTE_UPDATE, CoreToolNames.EXECUTE_QUERY, classificationResult,
                    createQuerySuggestedArguments(toolArguments, classificationResult));
        }
        return classificationResult;
    }
    
    private String resolveExecutionMode(final MCPToolArguments toolArguments) {
        String result = toolArguments.getStringArgument(MCPPayloadFieldNames.EXECUTION_MODE);
        if (result.isEmpty()) {
            throw new MCPExecutionModeRequiredException(CoreToolNames.EXECUTE_UPDATE, EXECUTION_MODES, createPreviewSuggestedArguments(toolArguments));
        }
        if (EXECUTION_MODE_EXECUTE.equals(result) || EXECUTION_MODE_PREVIEW.equals(result)) {
            return result;
        }
        throw new MCPInvalidExecutionModeException(CoreToolNames.EXECUTE_UPDATE, EXECUTION_MODES, createPreviewSuggestedArguments(toolArguments));
    }
    
    private MCPSuccessPayload createPreviewResponse(final MCPToolArguments toolArguments, final ClassificationResult classificationResult) {
        Map<String, Object> result = new LinkedHashMap<>(17, 1F);
        result.put("response_mode", MCPResponseMode.PREVIEW);
        result.put("result_kind", RESULT_KIND_PREVIEW);
        result.put(MCPPayloadFieldNames.EXECUTION_MODE, EXECUTION_MODE_PREVIEW);
        result.put("preview_semantics", "classification_only");
        result.put("affected_rows_estimated", false);
        result.put("status", "PREVIEWED");
        result.put("would_execute", false);
        result.put("statement_class", classificationResult.getStatementClass().name().toLowerCase(Locale.ENGLISH));
        result.put("statement_type", classificationResult.getStatementType());
        result.put("normalized_sql", classificationResult.getNormalizedSql());
        result.put("side_effect_scope", createSideEffectScope(classificationResult));
        classificationResult.getTargetObjectName().ifPresent(optional -> result.put("target_object", optional));
        classificationResult.getSavepointName().ifPresent(optional -> result.put("savepoint", optional));
        result.put("review_guidance", createReviewGuidance(classificationResult));

View on GitHub (pinned to e952770a21)

Solutions

  1. Use exactly 'execute' or 'preview' (lowercase, no whitespace).
  2. Map client-side enums: dryRun->'preview', everything-else->'execute' explicitly.
  3. Trim and lowercase execution_mode before sending.

Example fix

// before
await tools.call('database_gateway_execute_update', { sql, execution_mode: 'dry-run' }); // must be one of [execute, preview]

// after
const mode = opts.dryRun ? 'preview' : 'execute';
await tools.call('database_gateway_execute_update', { sql, execution_mode: mode });
Defensive patterns

Strategy: validation

Validate before calling

const EXECUTION_MODES = new Set(['execute', 'preview']);
function normalizeMode(raw) {
  const mode = String(raw ?? '').trim().toLowerCase();
  if (!EXECUTION_MODES.has(mode)) throw new Error(`execution_mode must be one of [execute, preview], got '${raw}'`);
  return mode;
}
const args = { sql, execution_mode: normalizeMode(userChoice) };

Type guard

function isExecutionMode(value) {
  return value === 'execute' || value === 'preview';
}

Try / catch

try {
  return await tools.call('database_gateway_execute_update', args);
} catch (e) {
  if (/execution_mode must be one of/.test(e.message)) {
    return tools.call('database_gateway_execute_update', { ...args, execution_mode: args.dryRun ? 'preview' : 'execute' });
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing execution_mode values such as 'EXECUTE' (wrong case), 'run', 'dry_run', 'dry-run', 'apply', or trailing whitespace — anything not exactly 'execute' or 'preview'.

Common situations: LLM paraphrasing the mode; enum mapping from a client that uses different names ('dry-run' for preview); case-sensitivity surprises; whitespace from templated payloads.

Related errors


AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14). Data as JSON: /api/errors/2905c1c4566e40eb. Report an issue: GitHub.