{"record":{"id":"e7f5d9330906f0e1","repo":"CoplayDev/unity-mcp","slug":"fal-phase-failed-status-res-status-detail","errorCode":null,"errorMessage":"fal {phase} failed (status={res?.Status}): {detail}","messagePattern":"fal (.+?) failed \\(status=(.+?)\\): (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"MCPForUnity/Editor/Services/AssetGen/Providers/FalAdapter.cs","lineNumber":170,"sourceCode":"            string single = result[\"image\"]?[\"url\"]?.ToString();\n            return string.IsNullOrEmpty(single) ? null : single;\n        }\n\n        private static JObject ParseOk(HttpResult res, string apiKey, string phase)\n        {\n            string text = ProviderHttp.BodyText(res);\n\n            JObject json = null;\n            if (!string.IsNullOrEmpty(text))\n            {\n                try { json = JObject.Parse(text); } catch { /* non-JSON */ }\n            }\n\n            bool ok = res?.Ok == true;\n            if (!ok)\n            {\n                string detail = json?[\"detail\"]?.ToString() ?? json?[\"error\"]?.ToString() ?? ProviderHttp.Truncate(text);\n                throw new Exception(SecretRedactor.Scrub($\"fal {phase} failed (status={res?.Status}): {detail}\", apiKey));\n            }\n            return json ?? new JObject();\n        }\n    }\n}\n","sourceCodeStart":152,"sourceCodeEnd":176,"githubUrl":"https://github.com/CoplayDev/unity-mcp/blob/c21bf496bca87d54e75bad048563c3adb1782081/MCPForUnity/Editor/Services/AssetGen/Providers/FalAdapter.cs#L152-L176","documentation":"ParseOk treats any HTTP response with res.Ok == false as a failure and throws a generic Exception containing the phase (submit/poll/download), the HTTP status, and a detail extracted from the JSON 'detail' or 'error' field (or the raw truncated text). The whole message is scrubbed of the apiKey.","triggerScenarios":"Any 4xx/5xx from fal during submit, poll, or download: 401/403 (auth), 422 (malformed body), 429 (rate limit), 5xx (server error), or a transport-level non-OK result.","commonSituations":"Invalid or expired API key; rate limiting; wrong model id or request schema; malformed request body; transient fal outage or gateway error.","solutions":["Read status + detail in the message to classify: 401/403 -> API key, 422 -> request schema, 429 -> rate limit, 5xx -> retry.","For 5xx and 429, retry with exponential backoff.","Verify the API key is valid and has remaining quota.","Ensure the model id and request body match fal's current schema."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-validate request inputs; HTTP failure itself cannot be pre-checked.\nif (string.IsNullOrEmpty(apiKey)) throw new ArgumentException(\"apiKey required\");\nif (string.IsNullOrEmpty(modelId)) throw new ArgumentException(\"modelId required\");","typeGuard":null,"tryCatchPattern":"for (int attempt = 0; ; attempt++)\n{\n    try { return await fal.ParseOk(res, apiKey, phase); }\n    catch (Exception ex) when (IsTransientHttp(ex) && attempt < 4) // 429/5xx\n    {\n        await Task.Delay((int)Math.Pow(2, attempt) * 500, ct);\n        continue;\n    }\n}","preventionTips":["Classify by status from the message: 401/403 -> key, 422 -> schema, 429/5xx -> retry.","Retry only transient statuses (429, 5xx) with exponential backoff.","Verify the API key is valid and has quota before retry storms."],"tags":["assetgen","fal","network","api","http","image"],"backgroundTag":null,"analyzedSha":"c21bf496bca87d54e75bad048563c3adb1782081","analyzedAt":"2026-08-13T17:36:56.095Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}