{"record":{"id":"0670fc28257cffa1","repo":"semaphoreui/semaphore","slug":"invalid-operation","errorCode":null,"errorMessage":"invalid operation","messagePattern":"invalid operation","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db/Store.go","lineNumber":159,"sourceCode":"\n// ObjectProps describe database entities.\n// It mainly used for NoSQL implementations (currently BoltDB) to preserve same\n// data structure of different implementations and easy change it if required.\ntype ObjectProps struct {\n\tTableName             string\n\tType                  reflect.Type // to which type the table bust be mapped.\n\tIsGlobal              bool         // doesn't belong to other table, for example to project or user.\n\tReferringColumnSuffix string\n\tPrimaryColumnName     string\n\tSortableColumns       []string\n\tDefaultSortingColumn  string\n\tSortInverted          bool // sort from high to low object ID by default. It is useful for some NoSQL implementations.\n\tOwnerships            []*ObjectProps\n\tSelectColumns         []string\n}\n\nvar ErrNotFound = errors.New(\"no rows in result set\")\nvar ErrInvalidOperation = errors.New(\"invalid operation\")\n\ntype TaskStatUnit string\n\nconst TaskStatUnitDay TaskStatUnit = \"day\"\nconst TaskStatUnitWeek TaskStatUnit = \"week\"\nconst TaskStatUnitMonth TaskStatUnit = \"month\"\n\ntype TaskFilter struct {\n\tStart  *time.Time `json:\"start\"`\n\tEnd    *time.Time `json:\"end\"`\n\tUserID *int       `json:\"user_id\"`\n\tStatus []task_logger.TaskStatus\n}\n\ntype TaskStat struct {\n\tDate          string                         `json:\"date\"`\n\tCountByStatus map[task_logger.TaskStatus]int `json:\"count_by_status\"`\n\tAvgDuration   int                            `json:\"avg_duration\"`","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/db/Store.go#L141-L177","documentation":"ErrInvalidOperation is the sentinel error ('invalid operation') returned by store/service methods when an operation is not permitted in the current state — e.g. deleting an environment that is still referenced by templates, or deleting integrations/matchers that are in use. The API layer maps it to HTTP 409 Conflict via WriteError, or a 400 with a domain message.","triggerScenarios":"environmentService.Delete for an environment used by one or more templates (api/projects/environment.go:299); DeleteIntegration, DeleteIntegrationExtractValue, DeleteIntegrationMatcher, or RemoveInventory on entities that are in use.","commonSituations":"Users trying to remove an environment still referenced by project templates; deleting an integration whose extracted values/matchers are wired into resources; cascading cleanup scripts hitting in-use entities.","solutions":["Delete the dependent resources first (e.g. remove templates referencing the environment) and retry","Check the sentinel with errors.Is(err, db.ErrInvalidOperation) to surface a 409/400 with a helpful message instead of a generic 500","Query the references (templates using the environment, integration usages) before attempting deletion to warn users up front"],"exampleFix":"// before\nif err := c.environmentService.Delete(env.ProjectID, env.ID); err != nil {\n    helpers.WriteError(w, err, http.StatusInternalServerError)\n}\n// after\nif err := c.environmentService.Delete(env.ProjectID, env.ID); err != nil {\n    if errors.Is(err, db.ErrInvalidOperation) {\n        helpers.WriteJSON(w, http.StatusBadRequest, map[string]any{\"error\": \"Environment is in use by one or more templates\"})\n        return\n    }\n    helpers.WriteError(w, err, http.StatusInternalServerError)\n}","handlingStrategy":"try-catch","validationCode":"// before deleting, check references\ntemplates, err := templateService.GetAll(env.ProjectID)\nif err == nil {\n    for _, t := range templates {\n        if usesEnvironment(t, env.ID) {\n            return errors.New(\"environment is in use by templates\")\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"err := c.environmentService.Delete(env.ProjectID, env.ID)\nif errors.Is(err, db.ErrInvalidOperation) {\n    helpers.WriteJSON(w, http.StatusBadRequest, map[string]any{\"error\": \"Environment is in use by one or more templates\"})\n    return\n}\nif err != nil {\n    helpers.WriteError(w, err.Error(), http.StatusInternalServerError)\n}","preventionTips":["Map db.ErrInvalidOperation to 409/400 responses so clients get actionable messages","Delete or reassign dependent resources before removing environments/integrations","Warn users in the UI when an entity is still referenced"],"tags":["database","conflict","sentinel-error"],"backgroundTag":"invalid-state-transition","analyzedSha":"1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa","analyzedAt":"2026-09-07T11:00:33.293Z","contentChangedAt":"2026-09-07T11:00:33.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}