{"record":{"id":"b5d890421b2ef97c","repo":"chenhg5/cc-connect","slug":"googlechat-read-credentials-file-w","errorCode":null,"errorMessage":"googlechat: read credentials_file: %w","messagePattern":"googlechat: read credentials_file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/googlechat/googlechat.go","lineNumber":101,"sourceCode":"func New(opts map[string]any) (core.Platform, error) {\n\tsubscription, _ := opts[\"subscription\"].(string)\n\tsubscription = strings.TrimSpace(subscription)\n\tif subscription == \"\" {\n\t\treturn nil, fmt.Errorf(\"googlechat: subscription is required (the Pub/Sub subscription your Chat app publishes to)\")\n\t}\n\tprojectID, err := projectFromSubscription(subscription)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcredentialsFile, _ := opts[\"credentials_file\"].(string)\n\tcredentialsFile = strings.TrimSpace(credentialsFile)\n\tif credentialsFile == \"\" {\n\t\treturn nil, fmt.Errorf(\"googlechat: credentials_file is required (the Chat app's service-account key, used to pull events and send replies)\")\n\t}\n\tkeyBytes, err := os.ReadFile(credentialsFile)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"googlechat: read credentials_file: %w\", err)\n\t}\n\tconf, err := google.JWTConfigFromJSON(keyBytes,\n\t\tchatBotScope, \"https://www.googleapis.com/auth/pubsub\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"googlechat: parse service account credentials: %w\", err)\n\t}\n\tbotClient := conf.Client(context.Background())\n\n\tallowFrom, _ := opts[\"allow_from\"].(string)\n\n\tcore.CheckAllowFrom(\"googlechat\", allowFrom)\n\n\treturn &Platform{\n\t\tsubscription:    subscription,\n\t\tprojectID:       projectID,\n\t\tcredentialsFile: credentialsFile,\n\t\ttokenSource:     conf.TokenSource(context.Background()),\n\t\tallowFrom:       allowFrom,","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/googlechat/googlechat.go#L83-L119","documentation":"googlechat.New reads the service-account key file with os.ReadFile and wraps any OS-level failure as 'googlechat: read credentials_file: %w'. This fires after credentials_file is non-empty, so the path was provided but could not be read.","triggerScenarios":"googlechat.New: os.ReadFile(credentialsFile) returns an error — file does not exist, permission denied on file or parent directories, path is a directory, or an I/O error — inside the platform constructor at startup.","commonSituations":"Typo in the path; relative path resolved against a different working directory when running under systemd; file mounted/secret not yet mounted at startup; restrictive file permissions after copying the key with umask 077 as a different user.","solutions":["Check the wrapped cause: 'no such file or directory' → fix the path; 'permission denied' → chmod/chown so the cc-connect process user can read the key","Use an absolute path in credentials_file instead of a relative one","Under systemd/daemon, confirm the key is mounted/available before the service starts and the WorkingDirectory is not misleading","Verify with ls -l <path> as the same user the daemon runs as"],"exampleFix":"// before\ncredentials_file = \"sa.json\"\n// after\ncredentials_file = \"/etc/cc-connect/sa.json\"  # absolute, readable by daemon user","handlingStrategy":"validation","validationCode":"// Go: check readability before New\nif info, err := os.Stat(path); err != nil {\n    return fmt.Errorf(\"credentials_file %q: %w\", path, err)\n} else if info.IsDir() {\n    return fmt.Errorf(\"credentials_file %q is a directory\", path)\n}\n// and confirm the daemon user can read it","typeGuard":null,"tryCatchPattern":"p, err := core.CreatePlatform(\"googlechat\", opts)\nif err != nil && strings.Contains(err.Error(), \"read credentials_file\") {\n    return fmt.Errorf(\"check that the service-account key path is absolute and readable by the cc-connect user: %w\", err)\n}","preventionTips":["Use absolute paths for credentials_file","Deploy keys via secret mounts that are ready before service start","Set file ownership/permissions for the daemon user explicitly (chown cc-connect:cc-connect; chmod 600)","Test `sudo -u cc-connect cat <keyfile>` after deployment"],"tags":["googlechat","service-account","filesystem","startup"],"backgroundTag":"file-not-found","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"}