{"record":{"id":"f93a87056075683e","repo":"thedotmack/claude-mem","slug":"anthropic-api-rejected-request-with-http-400-this","errorCode":null,"errorMessage":"Anthropic API rejected request with HTTP 400: this model does not support the `effort` parameter. CLAUDE_CODE_EFFORT_LEVEL is likely leaking into the SDK subprocess env via ~/.claude-mem/.env — remove it or scope it to models that support effort. See https://github.com/thedotmack/claude-mem/issues/2357.","messagePattern":"Anthropic API rejected request with HTTP 400: this model does not support the `effort` parameter\\. CLAUDE_CODE_EFFORT_LEVEL is likely leaking into the SDK subprocess env via ~/\\.claude-mem/\\.env — remove it or scope it to models that support effort\\. See https://github\\.com/thedotmack/claude-mem/issues/2357\\.","errorType":"console","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"src/services/worker/ClaudeProvider.ts","lineNumber":138,"sourceCode":"  // the pattern in GeminiProvider.classifyGeminiError / classifyOpenRouterError\n  // (see #2357: the SDK forwards `effort` to the Messages API when\n  // CLAUDE_CODE_EFFORT_LEVEL leaks into the subprocess env, and models like\n  // Haiku/Sonnet 4.5 reject with 400 — without this branch the default\n  // `transient` classification retried indefinitely).\n  if (errAny.status === 400) {\n    // Inspect both the message and any structured body for the effort marker.\n    const bodyText = (() => {\n      const body = errAny.body;\n      if (typeof body === 'string') return body;\n      if (body && typeof body === 'object') {\n        try { return JSON.stringify(body); } catch { return ''; }\n      }\n      return '';\n    })();\n    const haystack = `${message}\\n${bodyText}`;\n    if (/effort parameter/i.test(haystack) && !effortHintLogged) {\n      effortHintLogged = true;\n      logger.warn(\n        'SDK',\n        'Anthropic API rejected request with HTTP 400: this model does not support the `effort` parameter. ' +\n          'CLAUDE_CODE_EFFORT_LEVEL is likely leaking into the SDK subprocess env via ~/.claude-mem/.env — ' +\n          'remove it or scope it to models that support effort. See https://github.com/thedotmack/claude-mem/issues/2357.',\n        { status: 400 }\n      );\n    }\n    return new ClassifiedProviderError(\n      message || 'Anthropic bad request (status 400)',\n      { kind: 'unrecoverable', cause: err },\n    );\n  }\n\n  // Status-less Anthropic 400s — SDK wrapping can drop `.status`, leaving only\n  // the message or an `invalid_request_error` body; classify those as\n  // unrecoverable so the worker stops retrying a permanent config error (#2656).\n  // The status guard keeps statused 4xx/5xx on their own branches.\n  if (","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/8bc631a71a487424b866756e43a6efa4574cc66b/src/services/worker/ClaudeProvider.ts#L120-L156","documentation":"The observer drives the Claude Agent SDK as a subprocess; that subprocess inherits claude-mem's environment, including ~/.claude-mem/.env. When that env contains CLAUDE_CODE_EFFORT_LEVEL and the active model does not support the effort parameter, the Anthropic API rejects requests with HTTP 400; the provider classifier detects the 'effort parameter' marker in the message or body, logs this hint once, and classifies the error as unrecoverable.","triggerScenarios":"~/.claude-mem/.env sets CLAUDE_CODE_EFFORT_LEVEL (e.g. low/medium/high) while the configured model rejects effort; the var was added for a different model and later the model changed; the SDK version started forwarding the var to the API.","commonSituations":"Users tuning effort for Claude Code in .env then switching observer models; copying a teammate's .env; upgrading the SDK so a previously ignored variable is now honored.","solutions":["Remove CLAUDE_CODE_EFFORT_LEVEL from ~/.claude-mem/.env and restart the worker","If you need effort, scope it to a model that supports the parameter (per-model env or settings) instead of the global .env","Verify the fix by confirming the observer no longer logs 400s and sessions complete"],"exampleFix":"# ~/.claude-mem/.env - before\nCLAUDE_CODE_EFFORT_LEVEL=low\n\n# after (line removed)\n# scope effort per supported model in the SDK settings instead","handlingStrategy":"validation","validationCode":"import fs from 'node:fs';\n\nfunction assertNoLeakedEffortLevel(envPath = path.join(os.homedir(), '.claude-mem', '.env')): void {\n  if (!fs.existsSync(envPath)) return;\n  const raw = fs.readFileSync(envPath, 'utf-8');\n  if (/^\\s*CLAUDE_CODE_EFFORT_LEVEL\\s*=/m.test(raw)) {\n    throw new Error('CLAUDE_CODE_EFFORT_LEVEL in ~/.claude-mem/.env breaks models without effort support — remove or scope it');\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await runObserverQuery(session);\n} catch (error: unknown) {\n  const msg = error instanceof Error ? error.message : String(error);\n  if (/effort parameter/i.test(msg)) {\n    // unrecoverable config issue: strip CLAUDE_CODE_EFFORT_LEVEL from .env, restart worker, do not retry as-is\n    throw new Error('Remove CLAUDE_CODE_EFFORT_LEVEL from ~/.claude-mem/.env (issue #2357)');\n  }\n  throw error;\n}","preventionTips":["Keep model-specific knobs like effort out of the global ~/.claude-mem/.env","After changing models, re-audit .env for variables the new model does not support","Restart the worker after env changes so the SDK subprocess inherits the fixed environment"],"tags":["anthropic-api","http-400","environment","sdk","effort-parameter"],"backgroundTag":"unsupported-api-parameter","analyzedSha":"8bc631a71a487424b866756e43a6efa4574cc66b","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}