{"record":{"id":"21a271f4751cfca7","repo":"decolua/9router","slug":"kimchi-token-validation-failed-validationres-s","errorCode":null,"errorMessage":"`Kimchi token validation failed: ${validationRes.status}`","messagePattern":"`Kimchi token validation failed: (.+?)`","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/lib/oauth/providers/kimchi.js","lineNumber":29,"sourceCode":"    });\n    return `${baseUrl}/cli-auth?${params.toString()}`;\n  },\n  exchangeToken: async (config, token) => {\n    const accessToken = String(token || \"\").trim();\n    if (!accessToken) {\n      throw new Error(\"Missing Kimchi token\");\n    }\n\n    const validationUrl = config.validationUrl || \"https://api.cast.ai/v1/llm/openai/supported-providers\";\n    const validationRes = await fetch(validationUrl, {\n      method: \"GET\",\n      headers: {\n        Accept: \"application/json\",\n        Authorization: `Bearer ${accessToken}`,\n      },\n    });\n    if (!validationRes.ok) {\n      throw new Error(`Kimchi token validation failed: ${validationRes.status}`);\n    }\n\n    let userInfo = {};\n    if (config.userInfoUrl) {\n      try {\n        const userRes = await fetch(config.userInfoUrl, {\n          method: \"GET\",\n          headers: {\n            Accept: \"application/json\",\n            Authorization: `Bearer ${accessToken}`,\n          },\n        });\n        if (userRes.ok) {\n          userInfo = await userRes.json();\n        }\n      } catch {\n        userInfo = {};\n      }","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/providers/kimchi.js#L11-L47","documentation":"Thrown when the Kimchi provider's token validation request — a GET to the CAST AI supported-providers endpoint (config.validationUrl) with the token as Bearer — returns a non-2xx status. The HTTP status is embedded in the message (e.g. 401, 403). This proves the token was present but rejected or the endpoint was unavailable.","triggerScenarios":"exchangeToken reaches the fetch and validationRes.ok is false: expired/revoked CAST AI token (401), token lacking required scopes (403), malformed token, network/proxy returning 5xx, or a custom validationUrl pointing at the wrong endpoint.","commonSituations":"Token copied from the wrong CAST AI org/API key; token rotated elsewhere after paste; corporate proxy intercepting api.cast.ai; typo in config.validationUrl override.","solutions":["Read the status in the message: 401/403 → generate a fresh CAST AI API token with correct permissions and retry.","Verify the token works directly: curl -H \"Authorization: Bearer <token>\" https://api.cast.ai/v1/llm/openai/supported-providers.","If a custom config.validationUrl is set, confirm it is the correct CAST AI validation endpoint and reachable.","Check network/proxy settings if the status is 5xx."],"exampleFix":"// before\nvalidationUrl: 'https://api.cast.ai/v1/wrong-endpoint'\n// after\nvalidationUrl: 'https://api.cast.ai/v1/llm/openai/supported-providers'","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await provider.exchangeToken(config, token);\n} catch (e) {\n  const m = e.message.match(/Kimchi token validation failed: (\\d+)/);\n  if (m) {\n    const status = Number(m[1]);\n    if (status === 401 || status === 403) {\n      // prompt user to regenerate the CAST AI token\n    } else {\n      // transient/network issue: back off and retry\n    }\n  } else throw e;\n}","preventionTips":["Pre-validate tokens with a direct curl against the supported-providers endpoint before saving them.","Track token creation dates and prompt rotation before expiry.","Keep config.validationUrl on the official CAST AI endpoint unless you truly need an override.","Surface the HTTP status from the message to the user to distinguish 401/403 from 5xx."],"tags":["oauth","http-401","token-validation","network"],"backgroundTag":"oauth-token-invalid","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}