thedotmack/claude-mem · error · AdapterRejectedInput
adapter rejected input: invalid_cwd
Error message
adapter rejected input: invalid_cwd
What it means
AdapterRejectedInput('invalid_cwd') thrown by antigravityCliAdapter.normalizeInput when the resolved working directory is not a non-empty string. isValidCwd only checks typeof === 'string' && length > 0, so the raw input cwd and all env fallbacks (GEMINI_CWD, GEMINI_PROJECT_DIR, CLAUDE_PROJECT_DIR, process.cwd()) must have failed to yield a usable value.
Solutions
- Pass a valid absolute directory string as cwd in the hook input JSON
- Export GEMINI_CWD or GEMINI_PROJECT_DIR with the project directory before invoking the CLI
- If cwd is meant to default, omit the cwd key entirely so process.cwd() is used instead of passing an empty string
- Wrap normalizeInput in try/catch on AdapterRejectedInput and log reason 'invalid_cwd' to diagnose which input field carried the bad value
Example fix
// before
const adapter = antigravityCliAdapter;
adapter.normalizeInput({ cwd: '' });
// after
adapter.normalizeInput({ cwd: '/absolute/path/to/project' }); Defensive patterns
Strategy: validation
Validate before calling
function safeCwd(v) { const c = typeof v === 'string' && v.length > 0 ? v : process.env.GEMINI_CWD || process.env.GEMINI_PROJECT_DIR || process.env.CLAUDE_PROJECT_DIR || process.cwd(); return c; }
if (!safeCwd(raw.cwd)) throw new Error('no usable cwd'); Type guard
function hasValidCwd(v: unknown): v is string { return typeof v === 'string' && v.length > 0; } Try / catch
try { adapter.normalizeInput(raw); } catch (e) { if (e instanceof AdapterRejectedInput && e.reason === 'invalid_cwd') { /* fix cwd or skip */ } else throw e; } Prevention
- Always pass an absolute directory string as cwd
- Never pass empty-string cwd; omit the key to use the process.cwd() fallback
- Set GEMINI_CWD/GEMINI_PROJECT_DIR in CI environments
- Sanitize hook JSON types before invoking the adapter
When it happens
Trigger: Calling the antigravity adapter with raw input where r.cwd is explicitly an empty string, whitespace-only, or a non-string (e.g. null, number), and none of GEMINI_CWD, GEMINI_PROJECT_DIR, or CLAUDE_PROJECT_DIR are set (process.cwd() itself always validates, so failure implies a corrupted/empty process.cwd or a cwd explicitly overridden with an empty value).
Common situations: Hook JSON from Antigravity containing "cwd": ""; test harnesses passing {cwd: undefined} while wiping env vars; Docker/CI environments where GEMINI_PROJECT_DIR points to a removed directory path that downstream code empties.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- adapter rejected input: invalid_cwd
- adapter rejected input: invalid_cwd
- adapter rejected input: invalid_cwd
- adapter rejected input: invalid_cwd
- adapter rejected input: invalid_cwd
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/899a73d88b30f953.
Report an issue: GitHub.
Appendix: source
Thrown at src/cli/adapters/antigravity-cli.ts:44
//
// formatOutput() must key its protojson response off the same raw payload, so we
// stash the last normalized raw input here (one normalizeInput→formatOutput
// cycle per hook process, so module state is safe).
let lastRawInput: Record<string, any> = {};
export const antigravityCliAdapter: PlatformAdapter = {
normalizeInput(raw) {
const r = (raw ?? {}) as any;
lastRawInput = r;
const cwd = r.workspacePaths?.[0]
?? r.cwd
?? process.env.GEMINI_CWD
?? process.env.GEMINI_PROJECT_DIR
?? process.env.CLAUDE_PROJECT_DIR
?? process.cwd();
if (!isValidCwd(cwd)) {
throw new AdapterRejectedInput('invalid_cwd');
}
const sessionId = r.conversationId
?? r.session_id
?? process.env.GEMINI_SESSION_ID
?? undefined;
const transcriptPath = r.transcriptPath ?? r.transcript_path;
const hasToolCall = Boolean(r.toolCall);
const hasErrorKey = 'error' in r;
// Pre/PostInvocation payloads are identical; both carry invocationNum and no
// toolCall.
const isInvocationEvent = 'invocationNum' in r && !hasToolCall;
let toolName: string | undefined = r.toolCall?.name ?? r.tool_name;
let toolInput: unknown = r.toolCall?.args ?? r.tool_input;
let toolResponse: unknown = r.tool_response;View on GitHub (pinned to d8bc9755e7)