{"record":{"id":"4137520f71ac0c7a","repo":"Wei-Shaw/sub2api","slug":"account-platform-is-required","errorCode":null,"errorMessage":"account platform is required","messagePattern":"account platform is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/internal/handler/admin/account_data.go","lineNumber":683,"sourceCode":"\tcase \"http\", \"https\", \"socks5\", \"socks5h\":\n\tdefault:\n\t\treturn fmt.Errorf(\"proxy protocol is invalid: %s\", item.Protocol)\n\t}\n\tif item.Status != \"\" {\n\t\tnormalizedStatus := normalizeProxyStatus(item.Status)\n\t\tif normalizedStatus != service.StatusActive && normalizedStatus != \"inactive\" {\n\t\t\treturn fmt.Errorf(\"proxy status is invalid: %s\", item.Status)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc validateDataAccount(item DataAccount) error {\n\tif strings.TrimSpace(item.Name) == \"\" {\n\t\treturn errors.New(\"account name is required\")\n\t}\n\tif strings.TrimSpace(item.Platform) == \"\" {\n\t\treturn errors.New(\"account platform is required\")\n\t}\n\tif strings.TrimSpace(item.Type) == \"\" {\n\t\treturn errors.New(\"account type is required\")\n\t}\n\tif len(item.Credentials) == 0 {\n\t\treturn errors.New(\"account credentials is required\")\n\t}\n\tswitch item.Type {\n\tcase service.AccountTypeOAuth, service.AccountTypeSetupToken, service.AccountTypeAPIKey, service.AccountTypeUpstream:\n\tdefault:\n\t\treturn fmt.Errorf(\"account type is invalid: %s\", item.Type)\n\t}\n\tif item.RateMultiplier != nil && *item.RateMultiplier < 0 {\n\t\treturn errors.New(\"rate_multiplier must be >= 0\")\n\t}\n\tif item.Concurrency < 0 {\n\t\treturn errors.New(\"concurrency must be >= 0\")\n\t}","sourceCodeStart":665,"sourceCodeEnd":701,"githubUrl":"https://github.com/Wei-Shaw/sub2api/blob/073e92d17178a1ccdb0a27017f572f10c9c7ab62/backend/internal/handler/admin/account_data.go#L665-L701","documentation":"Returned by validateDataAccount (backend/internal/handler/admin/account_data.go:683) when an accounts[] entry has a non-empty name but its platform is empty after TrimSpace. Platform identifies which upstream the account belongs to and is required on every imported account.","triggerScenarios":"An account object with name and type but platform missing/blank; a key renamed (e.g. 'provider') so platform is never set.","commonSituations":"Schema drift between export tools; hand-written payloads guessing field names; whitespace platform values from forms.","solutions":["Set platform to the expected platform identifier (e.g. \"openai\") on the entry.","Check the export/import schema docs for the exact field name and allowed values.","Map renamed fields when converting from other formats."],"exampleFix":"// before\n{ \"name\": \"main\", \"type\": \"oauth\", \"credentials\": { ... } }\n\n// after\n{ \"name\": \"main\", \"platform\": \"openai\", \"type\": \"oauth\", \"credentials\": { ... } }","handlingStrategy":"validation","validationCode":"function hasAccountPlatform(entry: Record<string, unknown>): boolean {\n  return typeof entry.platform === 'string' && entry.platform.trim() !== ''\n}","typeGuard":"function hasRequiredAccountStrings(e: unknown, keys: string[]): e is Record<string, string> {\n  if (typeof e !== 'object' || e === null) return false\n  const o = e as Record<string, unknown>\n  return keys.every((k) => typeof o[k] === 'string' && (o[k] as string).trim() !== '')\n}","tryCatchPattern":null,"preventionTips":["Map renamed source fields ('provider', 'vendor') to 'platform' during conversion.","Validate against the documented platform identifier list before upload.","Keep exports and imports on the same backend version to avoid schema drift."],"tags":["admin","import","account","validation","backend"],"backgroundTag":null,"analyzedSha":"073e92d17178a1ccdb0a27017f572f10c9c7ab62","analyzedAt":"2026-08-15T14:33:00.750Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}