{"record":{"id":"596ce0382eae6a23","repo":"actualbudget/actual","slug":"field-typedkey-is-system-managed-and-not-user-e","errorCode":null,"errorMessage":"Field ${typedKey} is system-managed and not user-editable.","messagePattern":"Field (.+?) is system-managed and not user-editable\\.","errorType":"validation","errorClass":"APIError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/api.ts","lineNumber":983,"sourceCode":"    const value = fields[typedKey];\n\n    switch (typedKey) {\n      case 'name': {\n        const newName = String(value);\n        const { data: existing } = await aqlQuery(\n          q('schedules').filter({ name: newName }).select('*'),\n        );\n        if (!existing || existing.length === 0 || existing[0].id === sched.id) {\n          sched.name = newName;\n          conditionsUpdated = true;\n        } else {\n          throw APIError(`There is already a schedule named: ${newName}`);\n        }\n        break;\n      }\n      case 'next_date':\n      case 'completed': {\n        throw APIError(\n          `Field ${typedKey} is system-managed and not user-editable.`,\n        );\n      }\n      case 'posts_transaction': {\n        sched.posts_transaction = Boolean(value);\n        conditionsUpdated = true;\n        break;\n      }\n      case 'payee': {\n        if (payeeIndex !== -1) {\n          sched._conditions[payeeIndex].value = value;\n          conditionsUpdated = true;\n        } else {\n          sched._conditions.push({\n            field: 'payee',\n            op: 'is',\n            value: String(value),\n          });","sourceCodeStart":965,"sourceCodeEnd":1001,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/api.ts#L965-L1001","documentation":"Schedule fields next_date and completed are derived by Actual from the schedule's date condition and completed status, not stored as freely editable fields. api.updateSchedule() rejects updates targeting these keys with this APIError.","triggerScenarios":"Calling api.updateSchedule(id, { next_date: '2026-01-01' }) or { completed: true } — the handler's switch on the field key hits the next_date/completed case and throws.","commonSituations":"Code that treats schedules like generic records and iterates over all field changes; bulk sync scripts copying schedule fields from another budget; attempts to mark schedules complete programmatically.","solutions":["Change next_date by editing the schedule's date condition (update the 'date' entry in sched._conditions / pass fields accepted for conditions)","Remove next_date/completed from the fields you send — let Actual compute them","If the goal is to pause a schedule, use the schedule's completed flag through the dedicated internal handler or the UI instead of updateSchedule"],"exampleFix":"// before\nawait api.updateSchedule(id, { next_date: '2026-09-01' });\n// after\nawait api.updateSchedule(id, {\n  conditions: [{ field: 'date', op: 'is', value: '2026-09-01' }],\n  conditionsOp: 'and',\n});","handlingStrategy":"validation","validationCode":"if (key === 'next_date' || key === 'completed') {\n  throw APIError(`Field ${key} is system-managed and not user-editable.`);\n}","typeGuard":"function isSystemManagedField(key) {\n  return key === 'next_date' || key === 'completed';\n}","tryCatchPattern":"try {\n  await updateSchedule(id, fields);\n} catch (e) {\n  if (/system-managed/.test(e.message)) {\n    const { [field]: _, ...rest } = fields;\n    return updateSchedule(id, rest);\n  }\n  throw e;\n}","preventionTips":["Publish an explicit READONLY_FIELDS list for schedules","Strip system fields from any generic diffing/sync code","Route next_date/completed changes through the date condition or dedicated handlers"],"tags":["api","schedules","read-only-field","validation"],"backgroundTag":"read-only-field","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}