{"record":{"id":"663b4c51d40405a5","repo":"multica-ai/multica","slug":"runtime-profile-is-disabled","errorCode":null,"errorMessage":"runtime profile is disabled","messagePattern":"runtime profile is disabled","errorType":"http","errorClass":null,"httpStatus":409,"severity":"error","filePath":"server/internal/handler/daemon.go","lineNumber":312,"sourceCode":"\t})\n\tif err != nil {\n\t\treturn rt\n\t}\n\tshared, ok := sharedDaemonCustomName(names)\n\tif !ok {\n\t\treturn rt\n\t}\n\tupdated, err := h.Queries.UpdateAgentRuntimeCustomName(ctx, db.UpdateAgentRuntimeCustomNameParams{\n\t\tCustomName: pgtype.Text{String: shared, Valid: true},\n\t\tID:         rt.ID,\n\t})\n\tif err != nil {\n\t\treturn rt\n\t}\n\treturn updated\n}\n\nvar errRuntimeProfileDisabled = errors.New(\"runtime profile is disabled\")\n\n// upsertRuntimeWithProfile serializes custom-runtime registration with profile\n// deletion. The profile row remains KEY SHARE locked until the runtime upsert\n// commits; DeleteRuntimeProfile takes a conflicting UPDATE lock before it\n// enumerates runtime rows. This closes the stale-read window where deletion\n// could miss an instance inserted by a concurrently registering daemon.\nfunc (h *Handler) upsertRuntimeWithProfile(\n\tctx context.Context,\n\tworkspaceID, profileID pgtype.UUID,\n\tbuild func(db.RuntimeProfile) db.UpsertAgentRuntimeWithProfileParams,\n) (db.UpsertAgentRuntimeWithProfileRow, db.RuntimeProfile, error) {\n\tvar row db.UpsertAgentRuntimeWithProfileRow\n\tvar profile db.RuntimeProfile\n\n\ttx, err := h.TxStarter.Begin(ctx)\n\tif err != nil {\n\t\treturn row, profile, fmt.Errorf(\"begin profile runtime registration: %w\", err)\n\t}","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/handler/daemon.go#L294-L330","documentation":"errRuntimeProfileDisabled is the sentinel returned by upsertRuntimeWithProfile when a daemon tries to register (upsert) a custom runtime against a runtime profile that has been disabled. Registration is serialized with profile deletion via row locks (the profile row holds a KEY SHARE lock until the upsert commits), so this error means the profile was found but is not in an enabled state — the daemon cannot bring up an instance for it. Callers match on this sentinel to return a distinct 'profile disabled' response rather than a generic failure.","triggerScenarios":"Daemon heartbeat/register call (PUT or upsert runtime) for a profileID whose profile row exists but is disabled — typically right after an admin disables the profile, or while a DeleteRuntimeProfile is racing the registration (the lock ordering closes the stale-read window, so the daemon gets a clean 'disabled' instead of registering a doomed instance).","commonSituations":"Admin disables a runtime profile while daemons are still running and auto-registering on heartbeat; a daemon caching an old profile list and retrying registration against a since-disabled profile; rollout flow where profiles are disabled before deletion and lagging daemons hit the window.","solutions":["On the daemon, stop retrying registration for that profileID and mark it locally disabled until the profile list refreshes.","Re-fetch the workspace's runtime profile list and drop profiles not enabled.","If the disable was unintentional, re-enable the profile server-side and registration will succeed on the next heartbeat.","Surface the 'profile disabled' distinction to operators instead of logging it as a generic registration failure."],"exampleFix":"// before\nfor {\n    err := registerRuntime(ctx, profileID)\n    if err != nil { time.Sleep(retryInterval) } // blind retry loop\n}\n\n// after\nerr := registerRuntime(ctx, profileID)\nif errors.Is(err, errRuntimeProfileDisabled) {\n    localProfiles.disable(profileID) // stop retrying; wait for profile refresh\n    return\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := h.upsertRuntimeWithProfile(ctx, workspaceID, profileID, build)\nif err != nil {\n    if errors.Is(err, errRuntimeProfileDisabled) {\n        // profile was disabled between listing and registration:\n        // stop retrying this profileID, mark it dead locally,\n        // and wait for the next profile list refresh\n        daemon.markProfileDisabled(profileID)\n        return nil\n    }\n    return fmt.Errorf(\"register runtime: %w\", err)\n}","preventionTips":["Match with errors.Is against the sentinel — do not string-compare the message.","Refresh the profile list before registration attempts after any disable/delete event.","Distinguish this from transient errors in daemon logs; it is terminal per-profile, not retryable.","On the server side, keep returning this sentinel (not a generic 500) so clients can react precisely."],"tags":["sentinel-error","runtime-registration","concurrency","lifecycle"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}