xportrs 0.0.8

CDISC-compliant XPT file generation and parsing library for Rust
Documentation
{{#title Troubleshooting - xportrs Guide}}

# Troubleshooting

This guide covers common issues and their solutions when working with xportrs.

## Validation Errors

### Variable Name Too Long

```
[ERROR] MYLONGVARNAME: Variable name exceeds 8 bytes
```

**Cause**: XPT V5 limits variable names to 8 bytes.

**Solution**: Shorten the variable name to ≤8 characters.

```rust,ignore
# use xportrs::{Column, ColumnData};
# let data = ColumnData::F64(vec![Some(1.0)]);
// Wrong
Column::new("MYLONGVARNAME", data.clone());

// Correct
Column::new("MYVAR", data);
```

### Variable Label Too Long

```
[ERROR] USUBJID: Variable label exceeds 40 bytes
```

**Cause**: XPT V5 limits labels to 40 bytes.

**Solution**: Shorten the label.

```rust,ignore
# use xportrs::{Column, ColumnData};
# let data = ColumnData::F64(vec![Some(1.0)]);
// Wrong (41 characters)
Column::new("VAR", data.clone())
    .with_label("This is a very long label that exceeds 40");

// Correct (40 characters max)
Column::new("VAR", data)
    .with_label("Unique Subject Identifier");
```

### Non-ASCII Characters (FDA)

```
[ERROR] AETERM: Variable label contains non-ASCII characters
```

**Cause**: FDA requires ASCII-only text.

**Solution**: Replace non-ASCII characters.

```rust,ignore
# use xportrs::{Column, ColumnData};
# let data = ColumnData::F64(vec![Some(1.0)]);
// Wrong
Column::new("VAR", data.clone())
    .with_label("Événement indésirable");

// Correct
Column::new("VAR", data)
    .with_label("Adverse Event");

// Or use a helper function
fn to_ascii(s: &str) -> String {
    s.chars().map(|c| match c {
        'é' | 'è' | 'ê' | 'ë' => 'e',
        'à' | 'â' | 'ä' => 'a',
        // ... more mappings
        c if c.is_ascii() => c,
        _ => '?',
    }).collect()
}
```

### Column Length Mismatch

```
Error: Column length mismatch: expected 100, got 99
```

**Cause**: Columns have different numbers of rows.

**Solution**: Ensure all columns have the same length.

```rust,ignore
# use xportrs::{Column, ColumnData, Dataset};
// Wrong
Dataset::new("AE", vec![
    Column::new("A", ColumnData::F64(vec![Some(1.0), Some(2.0)])),  // 2 rows
    Column::new("B", ColumnData::F64(vec![Some(1.0)])),              // 1 row!
]);

// Correct - same length
Dataset::new("AE", vec![
    Column::new("A", ColumnData::F64(vec![Some(1.0), Some(2.0)])),
    Column::new("B", ColumnData::F64(vec![Some(1.0), Some(2.0)])),
]);
```

## Warnings

### Missing Variable Label

```
[WARN] MYVAR: Variable 'MYVAR' is missing a label
```

**Cause**: Variable has no label defined.

**Solution**: Add a label.

```rust,ignore
# use xportrs::{Column, ColumnData};
# let data = ColumnData::F64(vec![Some(1.0)]);
Column::new("MYVAR", data)
    .with_label("My Variable Description");
```

### Missing Dataset Label

```
[WARN] AE: Dataset is missing a label
```

**Cause**: Dataset has no label defined.

**Solution**: Use `with_label` or `set_label`.

```rust,ignore
# use xportrs::{Column, ColumnData, Dataset};
# let columns = vec![Column::new("A", ColumnData::F64(vec![Some(1.0)]))];
// At construction
Dataset::with_label("AE", "Adverse Events", columns.clone());

// Or after
let mut ds = Dataset::new("AE", columns)?;
ds.set_label("Adverse Events");
```

## Reading Errors

### File Not Found

```
Error: No such file or directory (os error 2)
```

**Solution**: Verify the file path exists.

```rust,ignore
use std::path::Path;

let path = "data.xpt";
if !Path::new(path).exists() {
    eprintln!("File not found: {}", path);
}
```

### Invalid XPT Format

```
Error: Invalid header record
```

**Cause**: File is not a valid XPT V5 file.

**Solution**: Verify the file:

- Check it's an XPT file (not XPT V8, SAS7BDAT, etc.)
- Ensure it's not corrupted
- Verify with hex dump that it starts with `HEADER RECORD`

```bash
# Check file header
xxd -l 80 suspect.xpt
```

### Member Not Found

```
Error: MemberNotFound { domain_code: "XX" }
```

**Cause**: Requested member doesn't exist in the file.

**Solution**: Check available members.

```rust,ignore
# use xportrs::Xpt;
# fn main() -> xportrs::Result<()> {
let info = Xpt::inspect("multi.xpt")?;
for name in info.member_names() {
    println!("Available: {}", name);
}
# Ok(())
# }
```

## Writing Errors

### Write Permission Denied

```
Error: Permission denied (os error 13)
```

**Solution**: Check file/directory permissions.

```rust,ignore
use std::fs;

let dir = "/output";
fs::create_dir_all(dir)?;  // Create if missing

// Check write permission
let test_file = format!("{}/test.tmp", dir);
match fs::write(&test_file, "test") {
    Ok(_) => { fs::remove_file(&test_file)?; }
    Err(e) => eprintln!("Cannot write to {}: {}", dir, e),
}
```

### Disk Full

```
Error: No space left on device (os error 28)
```

**Solution**: Free disk space or write to a different location.

## Data Issues

### Precision Loss

```
// Original: 3.141592653589793
// After roundtrip: 3.141592653589792
```

**Cause**: IBM floating-point has slightly less precision than IEEE 754.

**Solution**: For critical values, store as strings or accept minor precision loss (~14-16 digits).

```rust,ignore
# use xportrs::{Column, ColumnData};
// Store as string for exact preservation
Column::new("EXACTVAL", ColumnData::String(vec![
    Some("3.141592653589793".into()),
]));
```

### Missing Values Handling

```rust,ignore
# use xportrs::ColumnData;
# let col_data = ColumnData::F64(vec![Some(1.0), None]);
// Check for missing values
if let ColumnData::F64(values) = &col_data {
    for (i, val) in values.iter().enumerate() {
        if val.is_none() {
            println!("Row {} is missing", i);
        }
    }
}
```

## Format Issues

### Invalid Format String

```
Error: Invalid format syntax: "DATE"
```

**Cause**: Format string missing trailing period.

**Solution**: SAS formats end with a period.

```rust,ignore
# use xportrs::Format;
// Wrong
Format::parse("DATE9");

// Correct
Format::parse("DATE9.");
```

### Format Not Preserved

**Cause**: Format might not be written if name is empty.

**Solution**: Use named formats.

```rust,ignore
# use xportrs::Format;
// May not be preserved (bare numeric format)
Format::parse("8.2");

// Will be preserved (named format)
Format::parse("BEST12.");
Format::parse("DATE9.");
Format::character(200);
```

## Performance Issues

### Slow Reading Large Files

**Solution**: Use row limiting for previews.

```rust,ignore
# use xportrs::Xpt;
# fn main() -> xportrs::Result<()> {
// Preview first 100 rows
let preview = Xpt::reader("large.xpt")
    .row_limit(100)
    .read()?;
# Ok(())
# }
```

### Memory Usage

**Solution**: Process in chunks for very large datasets.

```rust,ignore
# use xportrs::{Dataset, Xpt};
# fn process(_ds: &Dataset) {}
# fn main() -> xportrs::Result<()> {
// Read, process, and release
{
    let dataset = Xpt::read("chunk1.xpt")?;
    process(&dataset);
} // dataset dropped, memory freed

{
    let dataset = Xpt::read("chunk2.xpt")?;
    process(&dataset);
}
# Ok(())
# }
```

## Pinnacle 21 Validation Failures

### SD0063: Label Mismatch

**Cause**: XPT label doesn't match define.xml.

**Solution**: Ensure labels are consistent.

```rust,ignore
# use xportrs::{Column, ColumnData};
# let data = ColumnData::String(vec![Some("001".into())]);
// Label should match define.xml exactly
Column::new("USUBJID", data)
    .with_label("Unique Subject Identifier");  // As in define.xml
```

### SD1001: Variable Name Invalid

**Cause**: Variable name doesn't follow SAS naming rules.

**Solution**: Use uppercase, alphanumeric, start with letter.

```rust,ignore
# use xportrs::{Column, ColumnData};
# let data = ColumnData::F64(vec![Some(1.0)]);
// Wrong
Column::new("1stVar", data.clone());   // Starts with number
Column::new("my-var", data.clone());   // Contains hyphen

// Correct
Column::new("FIRSTVAR", data.clone());
Column::new("MYVAR", data);
```

## Getting Help

If you encounter issues not covered here:

1. Check the [API documentation]../api/dataset.md
2. Review the [XPT format specification]../format/structure.md
3. Open an issue on [GitHub]https://github.com/rubentalstra/xportrs/issues

When reporting issues, include:

- xportrs version
- Rust version
- Minimal code to reproduce
- Error messages
- Sample data (if not confidential)