{"record":{"id":"02b247ef8697d9c8","repo":"chenhg5/cc-connect","slug":"claudesession-write-per-spawn-prompt-file-w","errorCode":null,"errorMessage":"claudeSession: write per-spawn prompt file: %w","messagePattern":"claudeSession: write per-spawn prompt file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/claudecode/session.go","lineNumber":315,"sourceCode":"\tvar promptFileIsShared bool\n\t// Issue #1655: when a.language is non-empty, this session gets the\n\t// localized cc-connect system prompt. When empty (legacy callers),\n\t// AgentSystemPromptForLang returns the English default — same bytes as\n\t// the pre-PR buildAppendSystemPrompt(core.AgentSystemPrompt(), ...) call.\n\tif appended := buildAppendSystemPrompt(core.AgentSystemPromptForLang(lang), platformPrompt, appendSystemPrompt); appended != \"\" {\n\t\tif platformPrompt == \"\" && appendSystemPrompt == \"\" {\n\t\t\tpath, err := ensureSharedSystemPromptFile(ccDataDir, appended)\n\t\t\tif err != nil {\n\t\t\t\tcancel()\n\t\t\t\treturn nil, fmt.Errorf(\"claudeSession: ensure shared prompt file: %w\", err)\n\t\t\t}\n\t\t\tpromptFilePath = path\n\t\t\tpromptFileIsShared = true\n\t\t} else {\n\t\t\tpath, err := writeTempAppendPromptFile(ccDataDir, appended)\n\t\t\tif err != nil {\n\t\t\t\tcancel()\n\t\t\t\treturn nil, fmt.Errorf(\"claudeSession: write per-spawn prompt file: %w\", err)\n\t\t\t}\n\t\t\tpromptFilePath = path\n\t\t}\n\t\tinnerArgs = append(innerArgs, \"--append-system-prompt-file\", promptFilePath)\n\t}\n\n\tif effort != \"\" {\n\t\tinnerArgs = append(innerArgs, \"--effort\", effort)\n\t}\n\tif maxContextTokens > 0 {\n\t\tinnerArgs = append(innerArgs, \"--max-context-tokens\", strconv.Itoa(maxContextTokens))\n\t}\n\n\t// outerArgs are understood by both the wrapper and Claude CLI directly.\n\tvar outerArgs []string\n\tif model != \"\" {\n\t\touterArgs = append(outerArgs, \"--model\", model)\n\t}","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/claudecode/session.go#L297-L333","documentation":"newClaudeSession (agent/claudecode/session.go:315) writes the per-spawn prompt file via writeTempAppendPromptFile when a platform prompt or append prompt IS configured (the non-shared path). On failure it cancels the session context and returns 'claudeSession: write per-spawn prompt file: %w'. Session creation aborts before the claude CLI is spawned.","triggerScenarios":"Calling StartSession with platformPrompt or appendSystemPrompt set, where writeTempAppendPromptFile fails at any step: temp file creation (O_CREATE fails), Chmod 0644, Close, or rename/write — causes include unwritable temp dir, chmod failure (see error 126), or fd exhaustion.","commonSituations":"TMPDIR pointing to a full or read-only volume; restrictive umask/ACLs or containers with noexec/non-chmod tmpfs; too many open files (EMFILE) under heavy session churn; Windows Chmod quirks.","solutions":["Read the wrapped cause: EMFILE → raise ulimit -n; ENOSPC → free temp space; EACCES → fix temp dir permissions.","Set TMPDIR to a writable local directory (e.g. /tmp) for the cc-connect process.","Retry session creation after transient resource exhaustion; per-spawn files are cleaned on failure.","Check for temp-file leaks (crashed spawns leaving files) filling the temp dir, and clean <tmp>/cc-connect* leftovers."],"exampleFix":"// before\n# systemd unit\nEnvironment=TMPDIR=/var/tmp-readonly  → write per-spawn prompt file: permission denied\n// after\nEnvironment=TMPDIR=/tmp\nExecStartPre=/usr/bin/mkdir -p /tmp && /usr/bin/chmod 1777 /tmp","handlingStrategy":"validation","validationCode":"tmp := os.TempDir()\nif st, err := os.Stat(tmp); err != nil || !st.IsDir() {\n    return fmt.Errorf(\"TMPDIR %s invalid\", tmp)\n}\nprobe, err := os.CreateTemp(tmp, \"cc-probe-*\")\nif err != nil {\n    return fmt.Errorf(\"cannot create temp files in %s: %w\", tmp, err)\n}\nprobe.Close(); os.Remove(probe.Name())","typeGuard":null,"tryCatchPattern":"sess, err := agent.StartSession(ctx, opts)\nif err != nil && strings.Contains(err.Error(), \"write per-spawn prompt file\") {\n    if errors.Is(err, syscall.EMFILE) { /* raise ulimit -n and retry */ }\n    return nil, fmt.Errorf(\"temp file creation failed; check TMPDIR writability/space: %w\", err)\n}","preventionTips":["Set TMPDIR to a writable, space-available local directory for the service.","Raise the open-file ulimit if running many concurrent sessions.","Clean stale per-spawn prompt files from crashed spawns.","Retry StartSession on transient resource-exhaustion causes (EMFILE/ENOSPC)."],"tags":["claudecode","filesystem","temp-file"],"backgroundTag":"file-write-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}