{"record":{"id":"09f73eae25454468","repo":"iflytek/astron-agent","slug":"update-content-is-empty","errorCode":null,"errorMessage":"update content is empty","messagePattern":"update content is empty","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/tenant/internal/dao/base.go","lineNumber":33,"sourceCode":"\tbuffer.WriteString(querySql)\n\tparams := make([]interface{}, 0, len(options))\n\tfor index, option := range options {\n\t\tsqlStr, param := option()\n\t\tparams = append(params, param...)\n\t\tif index == 0 {\n\t\t\tbuffer.WriteString(\" where \")\n\t\t\tbuffer.WriteString(sqlStr)\n\t\t\tcontinue\n\t\t}\n\t\tbuffer.WriteString(\" and \")\n\t\tbuffer.WriteString(sqlStr)\n\t}\n\treturn buffer.String(), params\n}\n\nfunc buildUpdate(updateSql string, options ...SqlOption) (string, []interface{}, error) {\n\tif len(options) == 0 {\n\t\treturn \"\", nil, fmt.Errorf(\"update content is empty\")\n\t}\n\tvar buffer bytes.Buffer\n\tparams := make([]interface{}, 0, len(options))\n\tfor index, option := range options {\n\t\ts, param := option()\n\t\tbuffer.WriteString(s)\n\t\tparams = append(params, param...)\n\t\tif index == len(options)-1 {\n\t\t\tcontinue\n\t\t}\n\t\tbuffer.WriteString(\",\\n\")\n\t}\n\treturn fmt.Sprintf(updateSql, buffer.String()), params, nil\n}\n\nfunc buildUpdateWithQuery(updateSql string, whereSql []SqlOption, setSql ...SqlOption) (string, []interface{}, error) {\n\tfinalSql, setParams, err := buildUpdate(updateSql, setSql...)\n\tif err != nil {","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/tenant/internal/dao/base.go#L15-L51","documentation":"buildUpdate assembles an UPDATE statement from a list of SqlOption column setters; when no options are supplied there are no SET columns, so it returns this error instead of generating invalid SQL (UPDATE ... with empty SET).","triggerScenarios":"Calling an Update* DAO method (via buildUpdateWithQuery or the anonymous wrapper) with zero SqlOption arguments, e.g. UpdateAppByID(ctx, id) with no WithColumn/WithXxx setters.","commonSituations":"Dynamic update code built the options slice conditionally and every field was unchanged/filtered out; a caller passed an empty map of column updates; a refactor dropped the option arguments.","solutions":["Guarantee at least one SET column is passed, e.g. always include update_time via an option.","In the caller, check len(options)==0 before invoking the update and return a no-op or a validation error.","If the update is dynamic, fall back to fetching the record first and skipping the query when nothing changed.","Wrap the DAO call and map this error to a 400-style 'no fields to update' response."],"exampleFix":"// before\nerr := dao.UpdateAppByID(ctx, appID, opts...) // opts may be empty\n// after\nif len(opts) == 0 {\n    return fmt.Errorf(\"no fields to update for app %s\", appID)\n}\nopts = append(opts, WithUpdateTime(time.Now()))\nerr := dao.UpdateAppByID(ctx, appID, opts...)","handlingStrategy":"validation","validationCode":"if len(opts) == 0 {\n    return fmt.Errorf(\"no update fields supplied\")\n}","typeGuard":null,"tryCatchPattern":"err := dao.UpdateX(ctx, id, opts...)\nif err != nil && err.Error() == \"update content is empty\" {\n    return nil // treat as no-op, nothing changed\n}","preventionTips":["Always append a last-updated option so dynamic update lists are never empty.","Skip the DAO call entirely when the computed change set is empty.","Centralize update building in one helper that enforces at least one SET column."],"tags":["go","database","sql","empty-input"],"backgroundTag":"empty-required-field","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}