{"record":{"id":"4cabcc3ddc139236","repo":"chenhg5/cc-connect","slug":"claudesession-ensure-shared-prompt-file-w","errorCode":null,"errorMessage":"claudeSession: ensure shared prompt file: %w","messagePattern":"claudeSession: ensure shared prompt file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/claudecode/session.go","lineNumber":307,"sourceCode":"\t//     per-spawn write, no cleanup needed.\n\t//   • 1% edge case (Slack/Weixin/MAX platform formatting or user-set\n\t//     append_system_prompt) — write a per-spawn temp file containing\n\t//     the merged content, removed on Close.\n\t//\n\t// Claude only reads the file at startup and never writes it, so the\n\t// shared file is safe under concurrent spawns.\n\tvar promptFilePath string\n\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 {","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/claudecode/session.go#L289-L325","documentation":"newClaudeSession (agent/claudecode/session.go:307) materializes the append system prompt before spawning the claude CLI. When neither a platform prompt nor an append prompt is configured, it reuses a shared prompt file created via ensureSharedSystemPromptFile; if that fails it cancels the context and returns 'claudeSession: ensure shared prompt file: %w'. This blocks session creation entirely — no claude process is started.","triggerScenarios":"Calling StartSession with no platformPrompt and no appendSystemPrompt, where ensureSharedSystemPromptFile fails: ccDataDir cannot be created (permission denied), the atomic write fails, chmod/chown fails, or the disk is full.","commonSituations":"ccDataDir under a read-only or root-owned path; running cc-connect as a service user without write access to the data dir; disk-full; SELinux/AppArmor blocking writes to the configured directory.","solutions":["Check the wrapped cause: if it's mkdir/write permission denied, chown the ccDataDir to the user running cc-connect.","Verify the cc data directory path in config exists and is writable: touch <ccDataDir>/.probe as the service user.","Free disk space if the cause is ENOSPC.","Configure a platformPrompt/appendSystemPrompt intentionally (which switches to the per-spawn temp file path) only if that is the desired design — the shared file is still required otherwise."],"exampleFix":"// before (systemd unit)\n[Service]\nUser=ccbot\n# data dir owned by root → ensureSharedSystemPromptFile fails\n// after\nsudo chown -R ccbot:ccbot /var/lib/cc-connect\n# or in config.toml point cc_data_dir somewhere the user owns\n[agents.claudecode]\n# data_dir = \"/home/ccbot/.cc-connect\"","handlingStrategy":"validation","validationCode":"info, err := os.Stat(ccDataDir)\nif err != nil || !info.IsDir() {\n    return fmt.Errorf(\"cc data dir %s missing\", ccDataDir)\n}\nif err := os.WriteFile(filepath.Join(ccDataDir, \".probe\"), nil, 0o644); err != nil {\n    return fmt.Errorf(\"cc data dir %s not writable by current user: %w\", ccDataDir, err)\n}","typeGuard":null,"tryCatchPattern":"sess, err := agent.StartSession(ctx, opts)\nif err != nil && strings.Contains(err.Error(), \"ensure shared prompt file\") {\n    return nil, fmt.Errorf(\"cannot write to cc data dir; check ownership/permissions of the configured data directory: %w\", err)\n}","preventionTips":["chown the cc data directory to the user running cc-connect before deployment.","Keep the data dir off read-only or noexec mounts.","Monitor disk space on the volume hosting the data dir.","Verify writability as the actual service user (sudo -u <svc> touch <dir>/.probe)."],"tags":["claudecode","filesystem","session-startup"],"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-14T05:17:10.506Z"}