{"record":{"id":"639e9869d2b6bc0c","repo":"apache/beam","slug":"output-type-must-be-specified-for-sql-transform","errorCode":null,"errorMessage":"output type must be specified for sql.Transform","messagePattern":"output type must be specified for sql\\.Transform","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/transforms/sql/sql.go","lineNumber":108,"sourceCode":"// Example:\n//\n//\tin := beam.Create(s, 1, 2, 3)\n//\tout := sql.Transform(s, \"SELECT COUNT(*) FROM t\",\n//\t    sql.Input(\"t\", in),\n//\t    sql.OutputType(reflect.TypeOf(int64(0))))\n//\t// `out` is a PCollection<int64> with a single element 3.\n//\n// If an expansion service address is not provided as an option, one will be\n// automatically started for the transform.\nfunc Transform(s beam.Scope, query string, opts ...Option) beam.PCollection {\n\to := &options{\n\t\tinputs: make(map[string]beam.PCollection),\n\t}\n\tfor _, opt := range opts {\n\t\topt(o)\n\t}\n\tif o.outType == nil {\n\t\tpanic(\"output type must be specified for sql.Transform\")\n\t}\n\n\tpayload := beam.CrossLanguagePayload(&sqlx.ExpansionPayload{\n\t\tQuery:   query,\n\t\tDialect: o.dialect,\n\t})\n\n\texpansionAddr := sqlx.DefaultExpansionAddr\n\tif o.expansionAddr != \"\" {\n\t\texpansionAddr = xlangx.Require(o.expansionAddr)\n\t}\n\n\tout := beam.CrossLanguage(s, sqlx.Urn, payload, expansionAddr, o.inputs, beam.UnnamedOutput(o.outType))\n\treturn out[graph.UnnamedOutputTag]\n}\n","sourceCodeStart":90,"sourceCodeEnd":124,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/transforms/sql/sql.go#L90-L124","documentation":"sql.Transform expands a SQL query into a cross-language pipeline segment; the expansion payload requires an output schema, supplied via an option. If no output type option was given, the constructor panics because it cannot describe the expansion result to the runner.","triggerScenarios":"Calling sql.Transform(scope, query) without the WithOutputType (output type) option, then building the pipeline — the check runs at graph-construction time in the calling main (or the test TestTransform_MissingOutputType).","commonSituations":"Porting examples where the option list is built conditionally, refactors that drop beamx/sql options, or users assuming the output schema is inferred from the query (it is not — it must be supplied client-side for cross-language expansion).","solutions":["Pass sql.WithOutputType(beam.StructWith(...)) or the equivalent output-schema option to Transform.","Derive the output type from a registered Go struct matching the query's result schema.","Check that your option-building code path always appends the output-type option.","Refer to the transforms/sql package docs/tests for the exact option name and usage."],"exampleFix":"// before\nsql.Transform(s, \"SELECT id, name FROM users\")\n// after\nsql.Transform(s, \"SELECT id, name FROM users\", sql.WithOutputType(beam.StructWith(reflect.TypeOf(User{}))))","handlingStrategy":"validation","validationCode":"// Guard before calling Transform\nfunc buildSQLTransform(s beam.Scope, query string, opts ...sql.Option) beam.PCollection {\n\thasOut := false\n\tfor _, o := range opts {\n\t\tif isOutputTypeOpt(o) {\n\t\t\thasOut = true\n\t\t}\n\t}\n\tif !hasOut {\n\t\topts = append(opts, sql.WithOutputType(beam.StructWith(reflect.TypeOf(Result{}))))\n\t}\n\treturn sql.Transform(s, query, opts...)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass sql.WithOutputType when using transforms/sql","Centralize sql.Transform calls in a helper that injects the output type","Review option-building code after refactors"],"tags":["go","apache-beam","sql","cross-language","panic"],"backgroundTag":"missing-required-option","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}