{"record":{"id":"29287a0476ac83bb","repo":"googleapis/mcp-toolbox","slug":"jarfiles-is-required-when-mainclass-is-provided","errorCode":null,"errorMessage":"jarFiles is required when mainClass is provided","messagePattern":"jarFiles is required when mainClass is provided","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/tools/serverlessspark/serverlesssparkcreatesparkbatch/serverlesssparkcreatesparkbatch.go","lineNumber":100,"sourceCode":"\t\treturn nil, fmt.Errorf(\"must provide either mainJarFile or mainClass\")\n\t}\n\tif mainJar != \"\" && mainClass != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot provide both mainJarFile and mainClass\")\n\t}\n\n\tsparkBatch := &dataproc.SparkBatch{}\n\tif mainJar != \"\" {\n\t\tsparkBatch.Driver = &dataproc.SparkBatch_MainJarFileUri{MainJarFileUri: mainJar}\n\t} else {\n\t\tsparkBatch.Driver = &dataproc.SparkBatch_MainClass{MainClass: mainClass}\n\t}\n\n\tif jarFileUris, ok := paramMap[\"jarFiles\"].([]any); ok {\n\t\tfor _, uri := range jarFileUris {\n\t\t\tsparkBatch.JarFileUris = append(sparkBatch.JarFileUris, fmt.Sprintf(\"%v\", uri))\n\t\t}\n\t} else if mainClass != \"\" {\n\t\treturn nil, fmt.Errorf(\"jarFiles is required when mainClass is provided\")\n\t}\n\n\tif args, ok := paramMap[\"args\"].([]any); ok {\n\t\tfor _, arg := range args {\n\t\t\tsparkBatch.Args = append(sparkBatch.Args, fmt.Sprintf(\"%v\", arg))\n\t\t}\n\t}\n\n\treturn &dataproc.Batch{\n\t\tBatchConfig: &dataproc.Batch_SparkBatch{\n\t\t\tSparkBatch: sparkBatch,\n\t\t},\n\t}, nil\n}\n","sourceCodeStart":82,"sourceCodeEnd":115,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/serverlessspark/serverlesssparkcreatesparkbatch/serverlesssparkcreatesparkbatch.go#L82-L115","documentation":"A Spark batch that runs via mainClass needs the class on the driver classpath, which comes from jarFileUris. BuildBatch enforces that when mainClass is provided and no 'jarFiles' array parameter is present, the request is invalid — the jar containing the class must be supplied. Note the check triggers when jarFiles is absent entirely (type assertion to []any fails), not when it is merely empty.","triggerScenarios":"Invoking create-spark-batch with mainClass set but with no 'jarFiles' parameter at all (key missing or not a []any array, e.g. a single string instead of a list).","commonSituations":"Users assuming the main class is already bundled in the cluster image; passing jarFiles as a comma-separated string rather than an array so the []any assertion fails; agents generating mainClass-only calls.","solutions":["Add a 'jarFiles' array parameter containing the URIs of JARs that provide the main class","If jarFiles was passed as a string, convert it to a JSON array (e.g. [\"gs://bucket/app.jar\"])","Alternatively, use mainJarFile instead of mainClass+jarFiles to point directly at the application JAR"],"exampleFix":"// before\nparams := map[string]any{\"mainClass\": \"com.example.Main\", \"jarFiles\": \"gs://b/app.jar\"}\n// after\nparams := map[string]any{\"mainClass\": \"com.example.Main\", \"jarFiles\": []any{\"gs://b/app.jar\"}}","handlingStrategy":"validation","validationCode":"cls, _ := params[\"mainClass\"].(string)\njars, ok := params[\"jarFiles\"].([]any)\nif cls != \"\" && (!ok || len(jars) == 0) {\n    return errors.New(\"jarFiles array required when mainClass is set\")\n}","typeGuard":"func hasJarFiles(params map[string]any) bool {\n    _, ok := params[\"jarFiles\"].([]any)\n    return ok\n}","tryCatchPattern":"res, tbErr := tool.Invoke(ctx, src, paramValues, token)\nif tbErr != nil && strings.Contains(tbErr.Error(), \"jarFiles is required when mainClass is provided\") {\n    // add jarFiles: [\"gs://...\"] and re-invoke\n}","preventionTips":["Always pass jarFiles as a JSON array, never a single string (array type assertion fails otherwise)","When using mainClass, ship the class inside a JAR uploaded to GCS and list its URI","Prefer mainJarFile when the app is a single self-contained JAR to sidestep this rule"],"tags":["validation","parameters","dataproc","serverlessspark"],"backgroundTag":"missing-required-argument","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}