{"record":{"id":"f70df65c80eea743","repo":"can1357/oh-my-pi","slug":"smithery-api-key-cannot-be-empty","errorCode":null,"errorMessage":"Smithery API key cannot be empty.","messagePattern":"Smithery API key cannot be empty\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/mcp/smithery-auth.ts","lineNumber":86,"sourceCode":"export async function getSmitheryApiKey(): Promise<string | undefined> {\n\tconst envKey = normalizeApiKey(process.env.SMITHERY_API_KEY);\n\tif (envKey) return envKey;\n\n\tconst authPath = getSmitheryAuthPath();\n\ttry {\n\t\tconst payload = (await Bun.file(authPath).json()) as SmitheryAuthPayload;\n\t\treturn normalizeApiKey(payload.apiKey);\n\t} catch (error) {\n\t\tif (isEnoent(error)) return undefined;\n\t\tlogger.warn(\"Failed to read Smithery auth file, treating as missing\", { path: authPath, error });\n\t\treturn undefined;\n\t}\n}\n\nexport async function saveSmitheryApiKey(apiKey: string): Promise<void> {\n\tconst normalized = normalizeApiKey(apiKey);\n\tif (!normalized) {\n\t\tthrow new Error(\"Smithery API key cannot be empty.\");\n\t}\n\n\tconst authPath = getSmitheryAuthPath();\n\tconst payload: SmitheryAuthPayload = { apiKey: normalized };\n\tawait Bun.write(authPath, `${JSON.stringify(payload, null, 2)}\\n`);\n\ttry {\n\t\tawait fs.chmod(authPath, 0o600);\n\t} catch (error) {\n\t\tlogger.warn(\"Could not set restrictive permissions on Smithery auth file\", { path: authPath, error });\n\t}\n}\n\nexport async function clearSmitheryApiKey(): Promise<boolean> {\n\tconst authPath = getSmitheryAuthPath();\n\ttry {\n\t\tawait fs.rm(authPath);\n\t\treturn true;\n\t} catch (error) {","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/mcp/smithery-auth.ts#L68-L104","documentation":"Validation error thrown by saveSmitheryApiKey when the provided key, after normalization (trimming whitespace), is an empty string. The function refuses to write a credentials file containing an empty key, which would cause every Smithery request to fail with auth errors later.","triggerScenarios":"Calling saveSmitheryApiKey(\"\") or saveSmitheryApiKey(\"   \") — typically when a user pastes nothing, presses Enter at a key prompt, or an environment-variable lookup returns an empty string.","commonSituations":"User skipped the API-key prompt in interactive login, SMITHERY_API_KEY env var set to empty string in CI, clipboard paste failed silently.","solutions":["Provide a real API key created in the Smithery dashboard (smithery.ai/settings/api-keys)","Check that SMITHERY_API_KEY is set to a non-empty value if relying on the environment","Re-run the login/prompt flow and actually paste the key","Trim the input in your calling code before saving"],"exampleFix":"// before\nconst key = process.env.SMITHERY_API_KEY ?? \"\";\nawait saveSmitheryApiKey(key);\n// after\nconst key = process.env.SMITHERY_API_KEY?.trim();\nif (!key) throw new Error(\"Set SMITHERY_API_KEY or paste a key from the Smithery dashboard\");\nawait saveSmitheryApiKey(key);","handlingStrategy":"validation","validationCode":"// validate before saving\nconst key = (userInput ?? process.env.SMITHERY_API_KEY ?? \"\").trim();\nif (!key) throw new Error(\"Refusing to save: Smithery API key is empty. Get one at smithery.ai settings.\");","typeGuard":"function isNonEmptyApiKey(v: unknown): v is string {\n  return typeof v === \"string\" && v.trim().length > 0;\n}","tryCatchPattern":"try {\n  await saveSmitheryApiKey(key);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"cannot be empty\")) {\n    console.error(\"No key provided — create one at smithery.ai and paste it\");\n    process.exitCode = 1;\n  } else throw err;\n}","preventionTips":["Always trim user/env input before saving keys","In CI, fail fast with a clear message when SMITHERY_API_KEY is unset/empty","Re-prompt instead of saving empty when an interactive paste fails","Verify the saved credentials file parses and has a non-empty apiKey after writing"],"tags":["validation","smithery","auth","api-key"],"backgroundTag":"missing-api-key","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}