slimtoolkit/slim · error

cannot open file %q to duplicate app's %s stream: %w

Error message

cannot open file %q to duplicate app's %s stream: %w

What it means

dupAppStdStream creates a log file in artifactsDir (app_<stdout|stderr>.log) to which the app's stream is duplicated. This error is returned when os.OpenFile fails to create/open that file, wrapping the underlying OS error.

Source

Thrown at pkg/app/sensor/monitor/composite.go:356

		PtReport:  ptReport,
	}, nil
}

func NonCriticalError(err error) error {
	return fmt.Errorf("non-critical monitor error: %w", err)
}

// Using simple io.MultiWriter(os.Stdout, os.File) would make cmd.Wait()
// block until either the cmd's stdout is closed or the multi-writer is closed.
// However, both are impossible. We need the Wait() to return much earlier
// than the process termination (see pkg/monitors/ptrace logic), and multi-writer
// cannot be closed at all. Hence, the pipe trick.
func dupAppStdStream(artifactsDir string, w io.Writer, kind string) (*os.File, *os.File, error) {
	filename := filepath.Join(artifactsDir, "app_"+kind+".log")

	f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0o644)
	if err != nil {
		return nil, nil, fmt.Errorf("cannot open file %q to duplicate app's %s stream: %w", filename, kind, err)
	}

	pr, pw, err := os.Pipe()
	if err != nil {
		f.Close()
		return nil, nil, fmt.Errorf("cannot create pipe for app %s stream: %w", kind, err)
	}

	go func() {
		n, err := io.Copy(io.MultiWriter(w, f), pr)
		log.Debugf("dupAppStdStream: io.Copy() finished; written=%d error=%v", n, err)
	}()

	return pw, f, nil
}

func closeAll(cs []io.Closer) {
	for _, c := range cs {

View on GitHub (pinned to 81940d17fa)

Solutions

  1. Ensure the artifacts directory exists and is writable by the user running the sensor (mkdir -p and chown/chmod as needed).
  2. Check the wrapped OS error for specifics (ENOENT, EACCES, ENOSPC) and fix accordingly.
  3. Verify there is free disk space and the filesystem is not mounted read-only.

Example fix

// before
NewCompositeMonitor(cfg{ArtifactsDir: "/nonexistent/dir"})
// after
os.MkdirAll("/var/log/artifacts", 0o755)
NewCompositeMonitor(cfg{ArtifactsDir: "/var/log/artifacts"})
Defensive patterns

Strategy: validation

Validate before calling

if info, err := os.Stat(artifactsDir); err != nil || !info.IsDir() {
    return fmt.Errorf("artifacts dir %q missing", artifactsDir)
}
probe, err := os.OpenFile(filepath.Join(artifactsDir, ".write_probe"), os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
    return fmt.Errorf("artifacts dir not writable: %w", err)
}
probe.Close()

Try / catch

mon, err := NewCompositeMonitor(cfg)
if err != nil {
    if strings.Contains(err.Error(), "cannot open file") {
        return fmt.Errorf("check artifacts dir permissions/disk space: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling NewCompositeMonitor when the artifacts directory does not exist, is not writable by the current user, is on a full/read-only filesystem, or the filename is invalid.

Common situations: artifactsDir pointing to a non-existent path or one created with restrictive permissions; running the sensor as a non-root user without write access; disk full; read-only container filesystem.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of slimtoolkit/slim@81940d17fa (2026-08-31). Data as JSON: /api/errors/37345b95cdfa4cde. Report an issue: GitHub.