v2rayA/v2rayA · error

tinytun route script (%s): %w

Error message

tinytun route script (%s): %w

What it means

runTinyTunScript wraps errors from resolveShellInfo with the stage name (e.g. pre/post route change) to give context about which script invocation failed. It means the shell type/path configured for tinytun route scripts could not be resolved into an executable spec (empty custom path or unknown shell type). No command was attempted yet.

Source

Thrown at service/kernel/v2ray/tinytun_enabled.go:729

	case "cmd":
		return shellInfo{bin: "cmd.exe", scriptFlag: "/C", ext: ".bat"}, nil
	case "git_bash":
		return shellInfo{bin: "bash.exe", ext: ".sh"}, nil
	default:
		return shellInfo{}, fmt.Errorf("unknown shell type: %v", shellType)
	}
}

// runTinyTunScript writes the given script to a temp file and executes it
// with the configured shell.  stage is only used for logging.
func runTinyTunScript(stage, script string) error {
	if strings.TrimSpace(script) == "" {
		return nil
	}
	setting := configure.GetSettingNotNil()
	si, err := resolveShellInfo(setting.TunRouteShellType, setting.TunRouteShellPath)
	if err != nil {
		return fmt.Errorf("tinytun route script (%s): %w", stage, err)
	}

	// Resolve the shell binary via PATH if it is not an absolute path.
	binPath := si.bin
	if !filepath.IsAbs(binPath) {
		if resolved, err := exec.LookPath(binPath); err == nil {
			binPath = resolved
		}
	}

	// Write script to a temp file with restricted permissions.
	tmpFile, err := os.CreateTemp("", "tinytun_*"+si.ext)
	if err != nil {
		return fmt.Errorf("tinytun route script (%s): failed to create temp file: %w", stage, err)
	}
	defer os.Remove(tmpFile.Name())

	// Restrict permissions immediately after creation (before writing content).

View on GitHub (pinned to 71e5442fc5)

Solutions

  1. Inspect the inner error in the %w chain: 'custom shell path is empty' → set the path; 'unknown shell type' → use a supported value.
  2. Correct TunRouteShellType/TunRouteShellPath in settings.
  3. Disable the tun route script for that stage if scripts are not needed.
Defensive patterns

Strategy: validation

Validate before calling

si, err := resolveShellInfo(setting.TunRouteShellType, setting.TunRouteShellPath)
if err != nil {
    return fmt.Errorf("tun route script disabled: %w", err)
}
if _, err := exec.LookPath(si.bin); err != nil {
    return fmt.Errorf("shell %q not found in PATH", si.bin)
}

Try / catch

if err := runTinyTunScript(stage, script); err != nil {
    log.Warnf("%v — skipping route script", err)
    return nil // degrade gracefully, keep tunnel running
}

Prevention

When it happens

Trigger: runTinyTunScript at tinytun_enabled.go:729 calls resolveShellInfo(setting.TunRouteShellType, setting.TunRouteShellPath) and receives an error — custom shell with empty path, or unknown shellType string.

Common situations: Same root causes as the underlying resolveShellInfo errors: blank custom shell path, misspelled or unsupported shell type in settings; surfacing after a tun route change triggers a script run.

Related errors


AI-assisted analysis of v2rayA/v2rayA@71e5442fc5 (2026-09-05). Data as JSON: /api/errors/29ce812b46dba296. Report an issue: GitHub.